mirror of
https://github.com/AK6DN/dec-utilities-for-pdp.git
synced 2026-01-11 23:42:54 +00:00
636 lines
26 KiB
Perl
636 lines
26 KiB
Perl
#!/usr/bin/perl -w
|
|
#!/usr/local/bin/perl -w
|
|
|
|
# options
|
|
use strict;
|
|
|
|
# external global modules
|
|
use Getopt::Long;
|
|
use Pod::Text;
|
|
use FindBin;
|
|
use FileHandle;
|
|
use File::Copy;
|
|
use File::Compare;
|
|
use POSIX;
|
|
use Expect;
|
|
|
|
# external local modules search path
|
|
BEGIN { unshift(@INC, $FindBin::Bin);
|
|
unshift(@INC, '.'); }
|
|
|
|
# external local modules
|
|
|
|
# defaults
|
|
my $VERSION = 'v1.0'; # version of code
|
|
my $HELP = 0; # set to 1 for man page output
|
|
my $DEBUG = 0; # set to 1 for debug messages
|
|
my $VERBOSE = 0; # set to 1 for verbose messages
|
|
|
|
# arguments
|
|
my $XMLFILE = undef; # define for XML file output
|
|
my $DEVICE = undef; # define for device selection as /dev/DEVICE
|
|
my $SLICE = undef; # define for device selection as $DEVICE.$SLICE
|
|
my $SCSI = undef; # list of scsi IDs per partition
|
|
my $READFILE = undef; # image file to read
|
|
my $WRITEFILE = undef; # image file to write
|
|
my $COMPAREFILE = undef; # image file to compare
|
|
my $PARTITIONS = undef; # list of data partitions to create
|
|
my $BOARDREV = 5; # board revision, 5 or 6
|
|
|
|
# process command line arguments
|
|
my $NOERROR = GetOptions( "help!" => \$HELP,
|
|
"debug!" => \$DEBUG,
|
|
"verbose!" => \$VERBOSE,
|
|
"xmlfile=s" => \$XMLFILE,
|
|
"readfile=s" => \$READFILE,
|
|
"writefile=s" => \$WRITEFILE,
|
|
"comparefile=s" => \$COMPAREFILE,
|
|
"device=s" => \$DEVICE,
|
|
"slice=i" => \$SLICE,
|
|
"scsi=s" => \$SCSI,
|
|
"partitions=s" => \$PARTITIONS,
|
|
"boardrev=i" => \$BOARDREV,
|
|
);
|
|
|
|
# init
|
|
$VERBOSE = 1 if $DEBUG; # debug implies verbose messages
|
|
|
|
# output the documentation
|
|
if ($HELP) {
|
|
# output a man page if we can
|
|
if (ref(Pod::Text->can('new')) eq 'CODE') {
|
|
# try the new way if appears to exist
|
|
my $parser = Pod::Text->new(sentence=>0, width=>78);
|
|
printf STDOUT "\n"; $parser->parse_from_file($0);
|
|
} else {
|
|
# else must use the old way
|
|
printf STDOUT "\n"; Pod::Text::pod2text(-78, $0);
|
|
};
|
|
exit(1);
|
|
}
|
|
|
|
# check for errors
|
|
$NOERROR = 0 if $BOARDREV < 5 || $BOARDREV > 6;
|
|
|
|
# check for correct arguments present, print usage if errors
|
|
unless ($NOERROR
|
|
&& scalar(@ARGV) == 0
|
|
&& defined($DEVICE)
|
|
) {
|
|
printf STDERR "%s %s (perl %g)\n", $0, $VERSION, $];
|
|
print STDERR "Usage: $0 [options...] arguments\n";
|
|
print STDERR <<"EOF";
|
|
--help output manpage and exit
|
|
--debug enable debug mode
|
|
--verbose verbose status reporting
|
|
--device=DEVICE device SDcard is mounted on (ie, sdd) [REQUIRED]
|
|
--slice=N logical partition number 5..8 [optional]
|
|
--boardrev=N PCB board rev, 5 (default) or 6 [optional]
|
|
--xmlfile=XMLFILE generated .xml description file [optional]
|
|
--readfile=IMGFILE image file to read from disk [optional]
|
|
--writefile=IMGFILE image file to write to disk [optional]
|
|
--comparefile=IMGFILE image file to compare to disk [optional]
|
|
--scsi=S1[,S2[,S3[,S4]]] scsi id per partition [optional]
|
|
--partitions=P1[,P2[,P3[,P4]]] logical partition sizes [optional]
|
|
EOF
|
|
# exit if errors...
|
|
die "Aborted due to command line errors.\n";
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# database setup
|
|
|
|
my %db = ();
|
|
my %slice = ();
|
|
my $devfile = '/dev/'.$DEVICE;
|
|
|
|
my %disktab = ( # type legacy#blocks assigned#blocks size-in-MB text-description
|
|
RL01 => { actual => 10240, allocate => 10240, capacity => 5, description => 'DEC RL01' },
|
|
RL02 => { actual => 20480, allocate => 20480, capacity => 10, description => 'DEC RL02' },
|
|
RD51 => { actual => 21600, allocate => 22528, capacity => 11, description => 'DEC RD51 Winchester' },
|
|
RK06 => { actual => 27126, allocate => 28672, capacity => 14, description => 'DEC RK06' },
|
|
RD31 => { actual => 41560, allocate => 43008, capacity => 21, description => 'DEC RD31 Winchester' },
|
|
RC25 => { actual => 50902, allocate => 51200, capacity => 25, description => 'RCF25' },
|
|
RK07 => { actual => 53790, allocate => 55296, capacity => 27, description => 'DEC RK07' },
|
|
RD52 => { actual => 60480, allocate => 61440, capacity => 30, description => 'DEC RD52 Winchester' },
|
|
RD32 => { actual => 83236, allocate => 83968, capacity => 41, description => 'DEC RD32 Winchester' },
|
|
RZ22 => { actual => 102432, allocate => 104448, capacity => 51, description => 'DEC RZ22 Winchester' },
|
|
AMP980 => { actual => 131680, allocate => 133120, capacity => 65, description => 'AMPEX DM980' },
|
|
RM03 => { actual => 131680, allocate => 133120, capacity => 65, description => 'DEC RM03' },
|
|
RD33 => { actual => 138565, allocate => 139264, capacity => 68, description => 'DEC RD33 Winchester' },
|
|
RD53 => { actual => 138672, allocate => 139264, capacity => 68, description => 'DEC RD53 Winchester' },
|
|
RP05 => { actual => 171798, allocate => 172032, capacity => 84, description => 'DEC RP05' },
|
|
RZ23 => { actual => 204864, allocate => 206848, capacity => 101, description => 'DEC RZ23 Winchester' },
|
|
RA80 => { actual => 237212, allocate => 237568, capacity => 116, description => 'DEC RA80 Winchester' },
|
|
RZ23L => { actual => 237588, allocate => 239616, capacity => 117, description => 'DEC RZ23L Winchester' },
|
|
RM80 => { actual => 242606, allocate => 243712, capacity => 119, description => 'DEC RM80' },
|
|
RB80 => { actual => 242606, allocate => 243712, capacity => 119, description => 'DEC R80 on 730 IDC' },
|
|
ESE20 => { actual => 245757, allocate => 245760, capacity => 120, description => 'DEC ESE20 Electronic' },
|
|
CDC9730 => { actual => 263360, allocate => 264192, capacity => 129, description => 'CDC 9730' },
|
|
FUJ160 => { actual => 263360, allocate => 264192, capacity => 129, description => 'Fujitsu 160' },
|
|
RF30 => { actual => 293040, allocate => 294912, capacity => 144, description => 'DEC RF30 Winchester' },
|
|
RD54 => { actual => 311200, allocate => 311296, capacity => 152, description => 'DEC RD54 Winchester' },
|
|
RP06 => { actual => 340670, allocate => 342016, capacity => 167, description => 'DEC RP06' },
|
|
RA60 => { actual => 400176, allocate => 401408, capacity => 196, description => 'DEC RA60 Removable' },
|
|
RZ24 => { actual => 409792, allocate => 411648, capacity => 201, description => 'DEC RZ24 Winchester' },
|
|
AMP9300 => { actual => 495520, allocate => 495616, capacity => 242, description => 'Ampex 9300' },
|
|
CDC9766 => { actual => 500384, allocate => 501760, capacity => 245, description => 'CDC 9766' },
|
|
RM05 => { actual => 500384, allocate => 501760, capacity => 245, description => 'DEC RM05' },
|
|
AMP330 => { actual => 524288, allocate => 524288, capacity => 256, description => 'Ampex Capricorn' },
|
|
RA70 => { actual => 547041, allocate => 548864, capacity => 268, description => 'DEC RA70 Winchester' },
|
|
RZ55 => { actual => 649040, allocate => 649216, capacity => 317, description => 'DEC RZ55 Winchester' },
|
|
RF31 => { actual => 744400, allocate => 745472, capacity => 364, description => 'DEC RF31 Winchester' },
|
|
RF71 => { actual => 781440, allocate => 782336, capacity => 382, description => 'DEC RF71 Winchester' },
|
|
EAGLE => { actual => 808320, allocate => 808960, capacity => 395, description => 'Fujitsu Eagle (48 sectors)' },
|
|
RZ25 => { actual => 832527, allocate => 833536, capacity => 407, description => 'DEC RZ25 Winchester' },
|
|
RA81 => { actual => 891072, allocate => 892928, capacity => 436, description => 'DEC RA81 Winchester' },
|
|
RP07 => { actual => 1008000, allocate => 1009664, capacity => 493, description => 'DEC RP07' },
|
|
CDC9775 => { actual => 1079040, allocate => 1079296, capacity => 527, description => 'CDC 9775' },
|
|
RA82 => { actual => 1216665, allocate => 1218560, capacity => 595, description => 'DEC RA82 Winchester' },
|
|
RZ56 => { actual => 1299174, allocate => 1300480, capacity => 635, description => 'DEC RZ56 Winchester' },
|
|
RZ80 => { actual => 1308930, allocate => 1310720, capacity => 640, description => 'Maxtor 8760 Winchester' },
|
|
RA71 => { actual => 1367310, allocate => 1368064, capacity => 668, description => 'DEC RA71 Winchester' },
|
|
RA72 => { actual => 1953300, allocate => 1953792, capacity => 954, description => 'DEC RA72 Winchester' },
|
|
RF72 => { actual => 1954050, allocate => 1955840, capacity => 955, description => 'DEC RF72 Winchester' },
|
|
RZ57 => { actual => 2025788, allocate => 2027520, capacity => 990, description => 'DEC RZ57 Winchester' },
|
|
M2266 => { actual => 2096256, allocate => 2097152, capacity => 1024, description => 'Fujitsu M2266' },
|
|
M2694 => { actual => 2117025, allocate => 2117632, capacity => 1034, description => 'Fujitsu M2694' },
|
|
RA90 => { actual => 2376153, allocate => 2377728, capacity => 1161, description => 'DEC RA90 Winchester' },
|
|
RZ58 => { actual => 2698061, allocate => 2699264, capacity => 1318, description => 'DEC RZ58 Winchester' },
|
|
RA92 => { actual => 2940951, allocate => 2942976, capacity => 1437, description => 'DEC RA92 Winchester' },
|
|
M2652 => { actual => 3409965, allocate => 3411968, capacity => 1666, description => 'Fujitsu M2652' },
|
|
RA73 => { actual => 3920490, allocate => 3921920, capacity => 1915, description => 'DEC RA73 Winchester' },
|
|
ST32171 => { actual => 4110000, allocate => 4110336, capacity => 2007, description => 'Seagate ST32171N' },
|
|
ST32550 => { actual => 4194995, allocate => 4196352, capacity => 2049, description => 'Seagate ST32550N' },
|
|
);
|
|
|
|
# defaults
|
|
$db{ByteCount} = undef;
|
|
$db{SectorCount} = undef;
|
|
$db{SectorSize} = 512;
|
|
$db{IDcode} = 0xDA;
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# initialize SDcard device structure
|
|
|
|
if (defined($DEVICE) && defined($PARTITIONS)) {
|
|
|
|
# setup for expect
|
|
my $prompt_cmd = qr/\nCommand \(m for help\): /;
|
|
my $timeout = 10;
|
|
|
|
# launch fdisk as an interactive subprocess
|
|
my $exp = Expect->spawn('/sbin/fdisk',
|
|
'--color=never', '--wipe=always', '--wipe-partitions=always',
|
|
$devfile) or die;
|
|
|
|
# enable debugging if set to 1
|
|
$exp->exp_internal(0);
|
|
|
|
# init MBR
|
|
$exp->expect($timeout,
|
|
[ $prompt_cmd, sub { $exp->send("o\n"); } ] );
|
|
|
|
# print disk info
|
|
$exp->expect($timeout,
|
|
[ $prompt_cmd, sub { $exp->send("p\n"); exp_continue; } ],
|
|
[ qr/\nDisk \S+: \S+ \S+, (\d+) bytes, (\d+) sectors/,
|
|
sub { $db{ByteCount} = ($exp->matchlist)[0];
|
|
$db{SectorCount} = ($exp->matchlist)[1];
|
|
$db{SectorSize} = int($db{ByteCount}/$db{SectorCount}); } ] );
|
|
|
|
# compute size of extra FAT partition as about 25MB; use rest for data partitions
|
|
my $use_size = int(25*1024*1024/$db{SectorSize});
|
|
|
|
# create logical container partition 1
|
|
$exp->expect($timeout,
|
|
[ $prompt_cmd, sub { $exp->send("n\n"); exp_continue; } ],
|
|
[ qr/\nSelect.+: /, sub { $exp->send("e\n"); exp_continue; } ],
|
|
[ qr/\nPartition.+: /, sub { $exp->send("1\n"); exp_continue; } ],
|
|
[ qr/\nFirst sector.+: /, sub { $exp->send("\n"); exp_continue; } ],
|
|
[ qr/\nLast sector.+: /, sub { $exp->send("-".$use_size."\n"); } ] );
|
|
|
|
# create filesystem partition 2
|
|
$exp->expect($timeout,
|
|
[ $prompt_cmd, sub { $exp->send("n\n"); exp_continue; } ],
|
|
[ qr/\nSelect.+: /, sub { $exp->send("p\n"); exp_continue; } ],
|
|
[ qr/\nPartition.+: /, sub { $exp->send("2\n"); exp_continue; } ],
|
|
[ qr/\nFirst sector.+: /, sub { $exp->send("\n"); exp_continue; } ],
|
|
[ qr/\nLast sector.+: /, sub { $exp->send("-8\n"); } ] );
|
|
|
|
# change filesystem partition 2 to FAT32
|
|
$exp->expect($timeout,
|
|
[ $prompt_cmd, sub { $exp->send("t\n"); exp_continue; } ],
|
|
[ qr/\nPartition.+: /, sub { $exp->send("2\n"); exp_continue; } ],
|
|
[ qr/\nHex code.+: /, sub { $exp->send("0b\n"); } ] );
|
|
|
|
# create data partitions
|
|
foreach my $entry (split(/,/,uc($PARTITIONS))) {
|
|
|
|
# partition size; lookup from table, else just use it
|
|
my $size = exists $disktab{$entry} ? $disktab{$entry}{allocate}-1 : $entry;
|
|
|
|
# created partition number
|
|
my $n = undef;
|
|
|
|
# create data partition N
|
|
$exp->expect($timeout,
|
|
[ $prompt_cmd, sub { $exp->send("n\n"); exp_continue; } ],
|
|
[ qr/\nSelect.+: /, sub { $exp->send("l\n"); exp_continue; } ],
|
|
[ qr/\nAdding logical partition (\d+)/,
|
|
sub { $n = ($exp->matchlist)[0]; exp_continue; } ],
|
|
[ qr/\nFirst sector.+: /, sub { $exp->send("\n"); exp_continue; } ],
|
|
[ qr/\nLast sector.+: /, sub { $exp->send("+".$size."\n"); } ] );
|
|
|
|
# change data partition N to raw data
|
|
if (defined($n)) {
|
|
$exp->expect($timeout,
|
|
[ $prompt_cmd, sub { $exp->send("t\n"); exp_continue; } ],
|
|
[ qr/\nPartition.+: /, sub { $exp->send($n."\n"); exp_continue; } ],
|
|
[ qr/\nHex code.+: /, sub { $exp->send(sprintf("%02x\n",$db{IDcode})); } ] );
|
|
}
|
|
|
|
# exit loop if created last partition
|
|
last if $n >= 8;
|
|
|
|
} # foreach my $entry
|
|
|
|
# print partitions
|
|
$exp->expect($timeout,
|
|
[ $prompt_cmd, sub { $exp->send("p\n"); } ] );
|
|
|
|
# write and quit
|
|
$exp->expect($timeout,
|
|
[ $prompt_cmd, sub { $exp->send("w\n"); } ] );
|
|
|
|
# and done
|
|
$exp->soft_close();
|
|
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# gather data from existing SDcard device
|
|
|
|
if (defined($DEVICE)) {
|
|
|
|
# read the partition map and get the partitions of the selected type
|
|
if (open(my $fh, '-|', '/sbin/fdisk --list '.$devfile)) {
|
|
|
|
# scan all lines
|
|
while (my $line = scalar(<$fh>)) {
|
|
$line =~ s/[\015\012]+$//;
|
|
if ($line =~ m/^${devfile}(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)/i) {
|
|
# process partition definition lines
|
|
my ($id, $start, $end, $count, $size, $type) = ($1+0,$2+0,$3+0,$4+0,$5,hex($6));
|
|
if ($type == $db{IDcode} && $count == $end-$start+1) {
|
|
$slice{$id}{start} = $start;
|
|
$slice{$id}{end} = $end;
|
|
$slice{$id}{count} = $count;
|
|
}
|
|
} elsif ($line =~ m/^disk\s+${devfile}:\s+[0-9.]+\s+[a-z]+,\s+(\d+)\s+bytes,\s+(\d+)\s+sectors/i) {
|
|
# process line with total physical device byte and sector count
|
|
$db{ByteCount} = ($1+0);
|
|
$db{SectorCount} = ($2+0);
|
|
$db{SectorSize} = int($db{ByteCount}/$db{SectorCount});
|
|
}
|
|
}
|
|
|
|
close($fh);
|
|
|
|
} # if (open(my $fh ...
|
|
|
|
if ($DEBUG) {
|
|
printf STDERR "\n[ SectorSize = %d ]\n", $db{SectorSize};
|
|
printf STDERR "[ SectorCount = %d ]\n", $db{SectorCount};
|
|
printf STDERR "[ ByteCount = %d ]\n\n", $db{ByteCount};
|
|
printf STDERR "[ Device Sector Sector Sector ]\n";
|
|
printf STDERR "[ & Slice Start End Count ]\n";
|
|
foreach my $id (sort(keys(%slice))) {
|
|
printf STDERR "[ %-10s %10d %10d %10d ]\n", $devfile.$id,
|
|
$slice{$id}{start}, $slice{$id}{end}, $slice{$id}{count};
|
|
}
|
|
printf STDERR "\n";
|
|
}
|
|
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# write image file to partition
|
|
|
|
if (defined($DEVICE) && defined($WRITEFILE) && defined($SLICE)) {
|
|
|
|
# some parameters
|
|
my $blksize = $db{SectorSize};
|
|
my $bufsize = 2048*$blksize;
|
|
|
|
# configure partition start/length blocks
|
|
my $start = $slice{$SLICE}{start};
|
|
my $end = $slice{$SLICE}{end};
|
|
my $count = $slice{$SLICE}{count};
|
|
|
|
# check input exists
|
|
if (open(my $ifh, '<', $WRITEFILE)) {
|
|
# check output exists
|
|
if (open(my $ofh, '+<', $devfile)) {
|
|
# seek to output position
|
|
die unless sysseek($ofh, $start*$blksize, 0) == $start*$blksize;
|
|
# this many bytes per chunk
|
|
my $buffer = undef;
|
|
# read/write byte counts
|
|
my $rdtotal = 0;
|
|
my $wrtotal = 0;
|
|
# read/write byte positions
|
|
my $rdpos = sysseek($ifh, 0, 1);
|
|
my $wrpos = sysseek($ofh, 0, 1);
|
|
while ((my $rdsize = sysread($ifh, $buffer, $bufsize)) > 0) {
|
|
# read bytes
|
|
$rdtotal += $rdsize;
|
|
printf STDERR "[ write slice %d rd %12d at %12d ]\n", $SLICE, $rdsize, $rdpos if $DEBUG;
|
|
# check write bytes vs partition size; set partition size as maximum
|
|
if ($wrtotal+$rdsize > $count*$blksize) {
|
|
$rdsize = $count*$blksize - $wrtotal;
|
|
printf STDERR "Write overflow; truncating read to %d bytes\n", $rdsize;
|
|
}
|
|
# write bytes
|
|
my $wrsize = syswrite($ofh, $buffer, $rdsize);
|
|
$wrtotal += $wrsize;
|
|
printf STDERR "[ write slice %d wr %12d at %12d ]\n", $SLICE, $wrsize, $wrpos if $DEBUG;
|
|
# continue
|
|
$rdpos = sysseek($ifh, 0, 1);
|
|
$wrpos = sysseek($ofh, 0, 1);
|
|
} # while ((my $rdsize ...
|
|
# summary
|
|
printf STDERR "Write image file %s %d bytes to slice %d %d bytes\n",
|
|
$WRITEFILE, $rdtotal, $SLICE, $wrtotal if $VERBOSE;
|
|
close($ofh);
|
|
} # if (open(my $ofh ...
|
|
close($ifh);
|
|
} # if (open(my $ifh ...
|
|
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# read partition to image file
|
|
|
|
if (defined($DEVICE) && defined($READFILE) && defined($SLICE)) {
|
|
|
|
# some parameters
|
|
my $blksize = $db{SectorSize};
|
|
my $bufsize = 2048*$blksize;
|
|
|
|
# configure partition start/length blocks
|
|
my $start = $slice{$SLICE}{start};
|
|
my $end = $slice{$SLICE}{end};
|
|
my $count = $slice{$SLICE}{count};
|
|
|
|
# check input exists
|
|
if (open(my $ifh, '+<', $devfile)) {
|
|
# create output file
|
|
if (open(my $ofh, '>', $READFILE)) {
|
|
# seek to input position
|
|
die unless sysseek($ifh, $start*$blksize, 0) == $start*$blksize;
|
|
# this many bytes per chunk
|
|
my $buffer = undef;
|
|
# read/write byte counts
|
|
my $rdtotal = 0;
|
|
my $wrtotal = 0;
|
|
# read/write byte positions
|
|
my $rdpos = sysseek($ifh, 0, 1);
|
|
my $wrpos = sysseek($ofh, 0, 1);
|
|
while ($rdtotal < $count*$blksize) {
|
|
# shrink buffer on last read if extends past partition
|
|
$bufsize = $count*$blksize - $rdtotal if $rdtotal+$bufsize > $count*$blksize;
|
|
# read bytes
|
|
my $rdsize = sysread($ifh, $buffer, $bufsize);
|
|
$rdtotal += $rdsize;
|
|
printf STDERR "[ read slice %d rd %12d at %12d ]\n", $SLICE, $rdsize, $rdpos if $DEBUG;
|
|
# write bytes
|
|
my $wrsize = syswrite($ofh, $buffer, $rdsize);
|
|
$wrtotal += $wrsize;
|
|
printf STDERR "[ read slice %d wr %12d at %12d ]\n", $SLICE, $wrsize, $wrpos if $DEBUG;
|
|
# continue
|
|
$rdpos = sysseek($ifh, 0, 1);
|
|
$wrpos = sysseek($ofh, 0, 1);
|
|
} # while ((my $rdsize ...
|
|
# summary
|
|
printf STDERR "Read slice %d %d bytes to image file %s %d bytes\n",
|
|
$SLICE, $rdtotal, $READFILE, $wrtotal if $VERBOSE;
|
|
close($ofh);
|
|
} # if (open(my $ofh ...
|
|
close($ifh);
|
|
} # if (open(my $ifh ...
|
|
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# read partition to temp file and compare to named file
|
|
|
|
if (defined($DEVICE) && defined($COMPAREFILE) && defined($SLICE)) {
|
|
|
|
# some parameters
|
|
my $blksize = $db{SectorSize};
|
|
my $bufsize = 2048*$blksize;
|
|
|
|
# configure partition start/length blocks
|
|
my $start = $slice{$SLICE}{start};
|
|
my $end = $slice{$SLICE}{end};
|
|
my $count = $slice{$SLICE}{count};
|
|
|
|
# locals
|
|
my $tmpfile = sprintf("img2sdcard_%s%d_%d.tmp", $DEVICE, $SLICE, $$);
|
|
|
|
# check input exists
|
|
if (open(my $ifh, '+<', $devfile)) {
|
|
# create output file
|
|
if (open(my $ofh, '>', $tmpfile)) {
|
|
# seek to input position
|
|
die unless sysseek($ifh, $start*$blksize, 0) == $start*$blksize;
|
|
# this many bytes per chunk
|
|
my $buffer = undef;
|
|
# read/write byte counts
|
|
my $rdtotal = 0;
|
|
my $wrtotal = 0;
|
|
# read/write byte positions
|
|
my $rdpos = sysseek($ifh, 0, 1);
|
|
my $wrpos = sysseek($ofh, 0, 1);
|
|
while ($rdtotal < $count*$blksize) {
|
|
# shrink buffer on last read if extends past partition
|
|
$bufsize = $count*$blksize - $rdtotal if $rdtotal+$bufsize > $count*$blksize;
|
|
# read bytes
|
|
my $rdsize = sysread($ifh, $buffer, $bufsize);
|
|
$rdtotal += $rdsize;
|
|
printf STDERR "[ read slice %d rd %12d at %12d ]\n", $SLICE, $rdsize, $rdpos if $DEBUG;
|
|
# write bytes
|
|
my $wrsize = syswrite($ofh, $buffer, $rdsize);
|
|
$wrtotal += $wrsize;
|
|
printf STDERR "[ read slice %d wr %12d at %12d ]\n", $SLICE, $wrsize, $wrpos if $DEBUG;
|
|
# continue
|
|
$rdpos = sysseek($ifh, 0, 1);
|
|
$wrpos = sysseek($ofh, 0, 1);
|
|
} # while ((my $rdsize ...
|
|
close($ofh);
|
|
} # if (open(my $ofh ...
|
|
close($ifh);
|
|
} # if (open(my $ifh ...
|
|
|
|
# now do the compare
|
|
if (-r $tmpfile && -r $COMPAREFILE) {
|
|
# compare only length of compare file
|
|
my $len = (stat($COMPAREFILE))[7];
|
|
# do the compare, print result
|
|
my $sts = system('/usr/bin/cmp', '-b', '-n '.$len, $tmpfile, $COMPAREFILE);
|
|
printf STDERR "Compare slice %d %d bytes to image file %s %d bytes; status is '%s'\n",
|
|
$SLICE, $len, $COMPAREFILE, $len, $sts == 0 ? 'files match' : 'FILES DIFFER';
|
|
# delete tmp file if no error and not debug
|
|
unlink($tmpfile) if -e $tmpfile && $sts == 0 && !$DEBUG;
|
|
}
|
|
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# write an XML description file if requested and device exists
|
|
|
|
if (defined($DEVICE) && defined($XMLFILE)) {
|
|
|
|
my $header = 'SCSI2SD';
|
|
my $config = ($BOARDREV <= 5) ? 'BoardConfig' : 'S2S_BoardCfg';
|
|
my $target = 'SCSITarget';
|
|
my $units = ($BOARDREV <= 5) ? 4 : 6;
|
|
|
|
if (open(my $fh, '>', $XMLFILE)) {
|
|
|
|
# BoardConfig
|
|
my %bc = (
|
|
# rev 5 or less
|
|
disableGlitchFilter => { rev => 5, value => 'false' },
|
|
enableCache => { rev => 5, value => 'false' },
|
|
enableDisconnect => { rev => 5, value => 'true' },
|
|
# any rev but variable default
|
|
enableScsi2 => { rev => 0, value => ($BOARDREV <= 5 ? 'false' : 'true') },
|
|
enableTerminator => { rev => 0, value => ($BOARDREV <= 5 ? 'false' : 'true') },
|
|
# any rev
|
|
mapLunsToIds => { rev => 0, value => 'false' },
|
|
parity => { rev => 0, value => 'true' },
|
|
scsiSpeed => { rev => 0, value => '0' },
|
|
selLatch => { rev => 0, value => 'false' },
|
|
selectionDelay => { rev => 0, value => '255' },
|
|
startupDelay => { rev => 0, value => '0' },
|
|
unitAttention => { rev => 0, value => 'true' },
|
|
);
|
|
|
|
# SCSITarget per unit
|
|
my %st = (
|
|
# any rev
|
|
enabled => { rev => 0, value => 'false' }, # overwritten
|
|
deviceType => { rev => 0, value => '0x0' },
|
|
deviceTypeModifier => { rev => 0, value => '0x0' },
|
|
#
|
|
bytesPerSector => { rev => 0, value => '512' },
|
|
headsPerCylinder => { rev => 0, value => '255' },
|
|
sectorsPerTrack => { rev => 0, value => '63' },
|
|
#
|
|
sdSectorStart => { rev => 0, value => sprintf("%d",0) }, # overwritten
|
|
scsiSectors => { rev => 0, value => sprintf("%d",100000) }, # overwritten
|
|
#
|
|
vendor => { rev => 0, value => 'SCSItoSD' },
|
|
prodId => { rev => 0, value => sprintf("%-16s",'RA81') }, # overwritten
|
|
revision => { rev => 0, value => '0001' },
|
|
serial => { rev => 0, value => sprintf("%-16d",12345) }, # overwritten
|
|
quirks => { rev => 0, value => '' },
|
|
# rev 5 or less
|
|
modePages => { rev => 5, value => '' },
|
|
vpd => { rev => 5, value => '' },
|
|
);
|
|
|
|
# XML header
|
|
printf $fh "<%s>\n", $header;
|
|
|
|
# board configuration
|
|
printf $fh " <%s>\n", $config;
|
|
foreach my $key (sort(keys(%bc))) {
|
|
printf $fh " <%s>%s</%s>\n", $key, $bc{$key}{value}, $key
|
|
if $bc{$key}{rev} == 0 || $bc{$key}{rev} == $BOARDREV;
|
|
}
|
|
printf $fh " </%s>\n", $config;
|
|
|
|
# list of defined units and corresponding scsi id
|
|
my @list = sort({$a <=> $b}keys(%slice));
|
|
my @scsi = defined($SCSI) ? (split(/,/,$SCSI)) : (0..$#list);
|
|
|
|
# mapped units in SCSItoSD
|
|
for (my $unit = 0; $unit < $units; $unit++) {
|
|
# get unit number from list
|
|
my $id = $list[$unit];
|
|
# (assume) unit is undefined/disabled
|
|
$st{enabled}{value} = 'false';
|
|
# configure partition start/length
|
|
$st{sdSectorStart}{value} = sprintf("%d", 4096);
|
|
$st{scsiSectors}{value} = sprintf("%d", 4096);
|
|
# fake device type
|
|
$st{prodId}{value} = sprintf("%-16s", '*DISABLED*');
|
|
# unique serial always
|
|
$st{serial}{value} = sprintf("%-16d", 1000+$unit);
|
|
# check if less than all units defined
|
|
if (defined($id)) {
|
|
# unit is defined/enabled
|
|
$st{enabled}{value} = 'true';
|
|
# configure partition start/length
|
|
$st{sdSectorStart}{value} = sprintf("%d", $slice{$id}{start});
|
|
$st{scsiSectors}{value} = sprintf("%d", $slice{$id}{count});
|
|
# search type database for next same or larger entry
|
|
my @types = sort({$disktab{$a}{allocate}<=>$disktab{$b}{allocate}}keys(%disktab));
|
|
foreach my $type (@types) {
|
|
$st{prodId}{value} = sprintf("%-16s", $type);
|
|
last if $disktab{$type}{allocate} >= $slice{$id}{count};
|
|
}
|
|
}
|
|
# get allocated scsi ID, else find an unused one
|
|
my $scsiID = $scsi[$unit];
|
|
# if it exists, use it
|
|
if (!defined($scsiID)) {
|
|
# else find next unused scsi ID
|
|
foreach my $try (0..7) {
|
|
# search all allocated IDs, find next non-match
|
|
if (grep($try == $_, @scsi) == 0) {
|
|
# found an unused ID, allocate it
|
|
$scsiID = $try;
|
|
$scsi[$unit] = $scsiID;
|
|
# and we are done
|
|
last;
|
|
}
|
|
}
|
|
# can't ever get here as there are =8 scsiIDs and <8 slots
|
|
}
|
|
# print per unit configuration
|
|
printf $fh " <%s id=\"%d\">\n", $target, $scsiID;
|
|
foreach my $key (sort(keys(%st))) {
|
|
printf $fh " <%s>%s</%s>\n", $key, $st{$key}{value}, $key
|
|
if $st{$key}{rev} == 0 || $st{$key}{rev} == $BOARDREV;
|
|
}
|
|
printf $fh " </%s>\n", $target;
|
|
} # foreach my $unit ...
|
|
|
|
# XML trailer
|
|
printf $fh "</%s>\n", $header;
|
|
|
|
# all done
|
|
close($fh);
|
|
} # if (open(my $fh ...
|
|
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
exit;
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# the end
|