I am (or was) attempting to reimplement the discid.c program that can be downloaded from freedb.org. I know I could use Inline, but want to see if I can do the full thing in perl. I got it to import some constants from some header files and I got it to read the toc header using ioctl. However, to read individual entries, I have to read information into a more complex structure using ioctl (the header was easy as it took the first two bytes from the returned scalar).
So, my question is, how do I read into a more complex structure. My C isn't as good as it could be but I have attempted to come up with a pack structure that mimics the struct but ioctl doesn't like it.
I have included the beginning of my script with some of the calling C code and the structs from linux/cdrom.h. Again, I know I could use Inline to do this and will do that if no answer works, but I would rather see if there is an answer for reading a complex structure via ioctl.
Note: it is the second ioctl call that fails.
#!/usr/bin/perl -w
use strict;
use vars qw();
use Fatal qw(open close ioctl);
### try to load some cdrom constants
if( ! eval { require "linux/cdrom.ph" } ){
my $error;
if( $@ =~ /^(Can\'t locate .+? in \@INC)/ ){
$error = $1 ."\n You may need to run h2ph.\n";
}else{
$error = $@;
}
print $error;
print "Continuing with hard coded constants...\n";
### copy from linux/cdrom.ph
eval 'sub CDROMREADTOCHDR () {0x5305;}' unless defined(&CDROMREADTOC
+HDR);
eval 'sub CDROMREADTOCENTRY () {0x5306;}' unless defined(&CDROMREADT
+OCENTRY);
eval 'sub CDROM_MSF () {0x02;}' unless defined(&CDROM_MSF);
}
### open the cdrom device
print "Opening cd device...\n";
my $device = "/dev/hdc"; # not portable...yet
open(_DEV,"<$device");
### read the toc range
print "Reading toc header...\n";
my $str = '';
ioctl(_DEV, CDROMREADTOCHDR(), $str);
my $track0 = ord( substr($str,0,1) );
my $trackn = ord( substr($str,1,1) );
print "Track range: [$track0][$trackn]\n";
### read the entries
print "Reading toc entries...\n";
for(my $i = $track0; $i <= $trackn; $i ++){
my $track = $i;
my $format = CDROM_MSF();
my $struct = "CC4C4CCCCiC";
my $str = pack($struct,$track,0,0,$format);
ioctl(_DEV, CDROMREADTOCENTRY(), $str);
my($_track,$adr,$ctrl,$_format,
$min,$sec,$frame,
$lba,
$datamode) = unpack($struct,$str);
print "$_track $adr $_format $min $sec $frame\n";
}
print "Done\n";
__END__
# Sample calling code and structs
# struct cdrom_tocentry tocentry;
# tocentry.cdte_track = i;
# tocentry.cdte_format = CDROM_MSF;
# ioctl(drive, CDROMREADTOCENTRY, &tocentry);
#
# struct cdrom_msf0
# {
# __u8 minute;
# __u8 second;
# __u8 frame;
# };
#
# /* Address in either MSF or logical format */
# union cdrom_addr
# {
# struct cdrom_msf0 msf;
# int lba;
# };
#
# struct cdrom_tocentry
# {
# __u8 cdte_track;
# __u8 cdte_adr :4;
# __u8 cdte_ctrl :4;
# __u8 cdte_format;
# union cdrom_addr cdte_addr;
# __u8 cdte_datamode;
# };
my @a=qw(random brilliant braindead); print $a[rand(@a)];