Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

ioctl into or from perl structs

by Rhandom (Curate)
on Jan 24, 2002 at 02:15 UTC ( [id://141043]=perlquestion: print w/replies, xml ) Need Help??

Rhandom has asked for the wisdom of the Perl Monks concerning the following question:

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)];

Replies are listed 'Best First'.
Re: ioctl into or from perl structs
by wog (Curate) on Jan 24, 2002 at 03:02 UTC
    First, you seem to not understand how to handle these lines:

    __u8 cdte_adr :4; __u8 cdte_ctrl :4;

    You are trying to unpack this with C4 which represents 4 one-byte values. The C code says that each of those represents one 4-bit value, thus both those values are probably packed with one-byte. (You'll need to seperate them later, with the various bitshifting operators, therefore. You can probably explore and see what part of the byte each value is stored in.). As for the integer that comes later, you'll probably need to add some bytes of padding (x in the format for unpack) so that it will be aligned propperly. On a 32-bit machine this would probably imply one byte of padding to align on a 32-bit boundry.

    I would strongly advise that you avoid trying to unpack structures yourself. How they are packed is not always easy to fiquire out and is often dependent on the architecture being used. In some cases, it may even vary from compiler to compiler on that architecture. Writing a little bit of C to do it is likely to be more portable most of the time.

    update: Oops. Forgot to talk about that union. The union can hold either the structure cdrom_addr or an int. Usually this means that it's packed such that storage is only allocated for one. This means you'll probably need to have to two formats one which does CCCx (for the struct, padding to match the size of the integer. You'll need more if your platform has ints larger then 32 bits.) and one with does i (for the integer) depending on which is there. This doesn't really seem ideal, however, given you have to look at the info in the struct to see which to use. One solution would be to have a format where you first unpack that field with the format a4 (update: where 4 may be another number if ints are larger then 32 bits), and then further unpack the results with the aforementioned formats. Or you could unpack it as an integer and use bit operations to extract the __u8 values, too.

Re: ioctl into or from perl structs
by Zaxo (Archbishop) on Jan 24, 2002 at 02:58 UTC

    Here's one way:

    my $msf0_fmt = "c3x"; # x because this is likely to # be padded. omit as needed my $cdrom_addr_fmt = "$msf0_fmt I"; my $cdrom_tocentry_fmt = "c4 $cdrom_addr_fmt c"; # will need padding # if struct cdte_addr + is larger my $str = pack $cdrom_toentry_fmt; # make the string long enough # ... # call the ioctl on $str my @data = unpack $cdrom_toentry_fmt, $str;
    You will probably need to adjust the formats a bit, they depend on how the data is packed by the C compiler.

    There is code on Zaxo's scratchpad which may help, too. It is a translation of the Linux bit-twiddling for constructing ioctl commands. I expect it would be simple to adapt it to other platforms.

    Update: Fixed an out of sequence line in the code. I've posted the scratchpad code at Linux ioctl command module.

    After Compline,
    Zaxo

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://141043]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (4)
As of 2024-04-23 15:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found