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

I'm having a problem with Device::USB, specifically I'm getting the following warning:

Argument "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0..." isn't numeric in subroutine entry at /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/Device/USB/Device.pm line 585, <DATA> line 1.

This is then followed by a Segmentation fault.

This code from Device::USB that is causing this is:

sub get_descriptor { my $self = shift; my $type = shift; my $index = shift; $self->_assert_open(); my $buf = "\0" x MAX_BUFFER_SIZE; my $retlen = Device::USB::libusb_get_descriptor( $self->{handle}, $type, $index, $buf, MAX_BUFFER_SIZE ); return if $retlen < 0; return substr( $buf, 0, $retlen ); }

The argument $buf ends up as (nil) in Device::USB::libusb_get_descriptor (which is an Inline::C function) so there appears to be some problem with the argument passing.

Is this a bug in the Device::USB package?

Thanks in advance for any advice.

Replies are listed 'Best First'.
Re: Problem trying to use Device::USB
by almut (Canon) on Mar 15, 2010 at 07:21 UTC

    Quick tip on debugging the Inline::C stuff (in case you aren't already aware of it):  add NOCLEAN to the generated Makefile (after perl Makefile.PL, before make). I.e.

    .pm.inl: $(PERL) -Mblib -MInline=NOISY,NOCLEAN,_INSTALL_ -MDevice::USB +-e1 0.31 $(INST_ARCHLIB) ^^^^^^^

    This way, the intermediately generated C code doesn't get cleaned up (in the _Inline subdir), so you can look at the respective wrapper functions — in this case (in USB.c)

    XS(XS_Device__USB_libusb_get_descriptor) { dXSARGS; if (items != 5) Perl_croak(aTHX_ "Usage: Device::USB::libusb_get_descriptor(de +v, type, index, buf, size)"); { void * dev = INT2PTR(void *,SvIV(ST(0))); unsigned char type = (unsigned char)SvUV(ST(1)); unsigned char index = (unsigned char)SvUV(ST(2)); void * buf = INT2PTR(void *,SvIV(ST(3))); // <-- !! int size = (int)SvIV(ST(4)); int RETVAL; dXSTARG; RETVAL = libusb_get_descriptor(dev, type, index, buf, size); XSprePUSH; PUSHi((IV)RETVAL); } XSRETURN(1); }

    (which confirms BrowserUk's analysis)

      Thanks heaps guys. Changing to a char * fixed the problem.

      I've reported this as a bug to the module's author.

Re: Problem trying to use Device::USB
by BrowserUk (Patriarch) on Mar 15, 2010 at 06:33 UTC

    The problem is that call to libusb_get_descriptor() is defined as taking a void* for the buf parameter:

    int libusb_get_descriptor( void *dev, unsigned char type, unsigned char index, void *buf, int s +ize )

    But the typemap for that is void *            T_PTR, and that translates as

    T_PTR     $var = INT2PTR($type,SvIV($arg)).

    Ie. it is expecting the pointer to be passed as an integer in the IV slot. Hence the "argument isn't numeric".

    In order to pass a buffer in the PV slot, as the perl code is doing, the type should be one of those that translates to

    T_PV    $var = ($type)SvPV_nolen($arg).

    Eg. char *            T_PV

    There seem to be several of the functions that make this error.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Problem trying to use Device::USB
by gwadej (Chaplain) on Jul 22, 2010 at 03:52 UTC

    I've just had to revisit the code after applying the patch that resulted from this conversation. I wanted to update this discussion in case someone else had a similar issue.

    The patched code was resulting in segfaults for some people. After reading the discussion and looking at the code more carefully, I realized that the C code was using void* in two different situations and that the reported type problem was only an issue for one of them.

    In C, it is not unusual to use void* for both an opaque pointer and for a generic buffer. However, when passing these through Inline::C, we get (and need) very different behaviors.

    All of the opaque pointer cases needed to remain void*, since the Perl code can never look inside the structure to inspect it directly. More importantly, we need to be returning the exact same pointer, not a different pointer with the same data, so the conversion to/from an IV makes sense here.

    On the other hand, any generic buffer that we are expecting the Perl code to inspect must be changed to a char* for the reasons outlined in this thread.

    Unfortunately, the submitted patch confused these uses in the opposite direction of my original mistake. Thanks to all of you for helping me understand this aspect of Inline::C somewhat better.

    G. Wade