p-rex has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,

I'm not proficient at all with ioctl calls in Perl, so maybe someone can enlighten me on this one. Given a kernel module to access a PCI card /dev/pcicard. The following C code is working for writing a value to an internal register of the PCI card. The following code is proven to work:
#include <stdio.h> #include <sys/ioctl.h> #include <fcntl.h> #include "pcicard.h" int main(int argc, char **argv) { u8 val; int fd; if ((fd = open('/dev/pcicard', O_RDWR)) == -1) { perror("open"); exit(1); } val = 0x03; ioctl(fd, PCICARD_SET_REGISTER, &val); close(fd); exit(1); }
Here's a snipplet from pcicard.h:
#define PCIMAX_IOC_NUM 'O' #define PCICARD_SET_REGISTER _IOR(PCICARD_IOC_NUM, 1, u8 *) #define PCICARD_GET_REGISTER _IOW(PCICARD_IOC_NUM, 2, u8 *)
After converting with h2ph and moving the relevant lines into the Perl script itself, it looks as follows:
#!/usr/bin/perl require 'sys/ioctl.ph'; require '_h2ph_pre.ph'; sub PCICARD_IOC_NUM () { ord('0'); } sub PCICARD_SET_REGISTER () { &_IOR( &PCICARD_IOC_NUM, 1, 'int' ); } sub PCICARD_GET_REGISTER () { &_IOW( &PCICARD_IOC_NUM, 2, 'int' ); } $value = 0x03; open(PCI, '+</dev/pcimax'); ioctl(PCI, &PCICARD_SET_REGISTER, \$value); close(PCI);
However, this does not work. The corresponding code in the kernel module is not being executed. Any hints what I'm missing here? Many thanks!
Sven

Retitled by davido per consideration vote.

Replies are listed 'Best First'.
Re: Help using ioctl to write a PCI device
by tachyon (Chancellor) on Nov 01, 2004 at 23:13 UTC

    I think the problem is that you are passing a ref to 0x03 when you want to pass the value itself. I have no idea if your conversion leads to &PCICARD_SET_REGISTER returning a value. This is simply an integer, so given you can check the value in your working C code, and check what you have in your Perl would be a good sanity check. Obviously these values need to be the same. Also you don't check your file open worked although I don't see why it should fail given you have sufficient permissions to do the open in your C. I would suggest:

    #!/usr/bin/perl require 'sys/ioctl.ph'; require '_h2ph_pre.ph'; sub PCICARD_IOC_NUM () { ord('0'); } sub PCICARD_SET_REGISTER () { &_IOR( &PCICARD_IOC_NUM, 1, 'int' ); } sub PCICARD_GET_REGISTER () { &_IOW( &PCICARD_IOC_NUM, 2, 'int' ); } # Sanity check..... print "PCICARD_SET_REGISTER is %d\n", &PCICARD_SET_REGISTER; # you may need $value = pack 'C', 0x03 # as this will pack 0x03 into an unsigned 8 bit (char) value $value = 0x03; open(PCI, '+</dev/pcimax') or die "Can't open card $!\n"; ioctl(PCI, &PCICARD_SET_REGISTER, $value) or die "ioctl error $!\n"; close(PCI);

    As a last resort you could just dump your working C code into an Inline::C function. Sometimes this is the easiest thing to do.

    cheers

    tachyon

      Got it!

      The PCICARD_SET_REGISTER() sub is computing a wrong value (-2147471359) while the working C code is computing -2147201279. The 'int' must be wrong. Never mind, for my purpose it's better to hardcode the number anyway.

      But in case anybody would like to answer the bonus question: What is the corresponding third argument to u8 in C?

      Peace!
      Sven
        [root@devel3 root]# grep "#define _IOR" `locate ioctl.h` /usr/include/asm/ioctl.h:#define _IOR(type,nr,size) _IOC(_IOC_READ +,(type),(nr),sizeof(size)) /usr/src/linux-2.4.18-3/include/asm-i386/ioctl.h:#define _IOR(type,nr, +size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) /usr/src/linux-2.4.18-27.7.x/include/asm-i386/ioctl.h:#define _IOR(typ +e,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) /usr/i386-glibc21-linux/include/asm/ioctl.h:#define _IOR(type,nr,size) + _IOC(_IOC_READ,(type),(nr),sizeof(size)) [root@devel3 root]#

        So the third argument is supposed to be a u8 which has a sizeof 1. You are passing it 'int' which has a sizeof 4 (probably). s/int/char/ might possibly fix it.

        cheers

        tachyon

      Thanks a lot for your suggestions!

      I've removed the file open check for readability, there's no problem with open() nor ioctl() on this level; the latter returns "0 but true".

      Your sanity check returns a whopping:
      PCIMAX_IOC_SET_REGISTER is -2147471359
      Packing the value (or not) does not help. The pass by reference I borrowed from some sample code. Yet never mind how it is passed, I don't get that bleedin' value over to the corresponding kernel module function.

      Inline::C might work, but there has to be another way, after all, it's a simple IO call :-)

      Your help is very much apprechiated!
      Sven
Re: Help using ioctl to write a PCI device
by Fletch (Bishop) on Nov 01, 2004 at 22:51 UTC

    You're not checking the return value from any of your calls. Add an ... or die "open /dev/pcimax failed: $!\n"; (adjusted approriately) and see if that's enlightening.