http://qs321.pair.com?node_id=11120089

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

Hi monkers, in these days I was building an XS library, based on the librtlsdr library ( github link: https://github.com/librtlsdr/librtlsdr ) for now, some functions works fine but the "rtlsdr_open" call gives me some problems. For starting the code (from the github library) referred to the call is:
RTLSDR_API int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index); # where rtlsdr_dev_t is initialized as: typedef struct rtlsdr_dev rtlsdr_dev_t;
I think that my implementation of rtlsdr_dev is wrong, in the XS code I wrote
int rtlsdr_open(dev, index) RTLSDRDevice_T **dev uint32_t index # where RTLSDRDEVICE_T is typedef rtlsdr_dev_t RTLSDRDevice_T;
and from the TYPEMAP file the structure are declared as:
RTLSDRDevice_T * T_PTRREF RTLSDRDevice_T ** T_PTRREF
now, the problem is that, when I run this code:
use SDR::RTLSDR qw(:all); # my library my $serial_number = "00000001"; # from dmesg output my $index = rtlsdr_get_index_by_serial( $serial_number ); # it returns + 0, this is not an error value so I think is good my $device = rtlsdr_get_device_name( $index ); print rtlsdr_open( $device , $index)
I have the following error:
SDR::RTLSDR::rtlsdr_close: dev is not of type RTLSDRDevice_TPtr at sdr +.pl line 7.
Now, I think that the result is related to some pointer error but also with:
print rtlsdr_open( \$device , $index)
I had the same error code. hope in some helps Thanks Edoardo M.

Replies are listed 'Best First'.
Re: help with XS pointers
by syphilis (Archbishop) on Jul 31, 2020 at 02:24 UTC
    If you're having trouble with the basic functionality, I would recommend that you get that basic functionality of your code sorted out and tested in an Inline::C script before you even write SDR::RTLSDR.
    That's an approach that has always worked quite well for me.

    It also means that you can provide a script that shows everything you're trying to do.
    It's pretty hard to work out exactly what you're doing from a few snippets.

    I assume your module will link to the librtlsdr library, and I'm wondering why your XS code appears to be creating/declaring its own rtlsdr_open() when that function is already (presumably) provided by that library and the librtlsdr header.

    Anyway, for an Inline::C script, you'd want something like:
    use strict; use warnings; use Inline C => Config => #CLEAN_AFTER_BUILD => 0, # BUILD_NOISY => 1, # display the compilation inc => '-I/path/to/librtlsdr_headers', libs => '-L/path/to/librtsldr_library, ; use Inline C => <<'EOC'; #include<librtlsdr_header.h> void foo(<args>) { /* do something that uses librtlsdr and print out a success/fail message */ } void bar(<args>) { /* do something else that uses librtlsdr and print out a success/fail message */ } EOC foo(); bar();
    Add more functionality and complexity as you get things working.
    Inline::C provides typemapping and just about everything else that XS provides. (See the Inline::C documentation and cookbook.)

    In the code section, you're essentially just writing C code - but perl API functions can also be used.

    Inline::C works by first creating an XS file, which you can find in the ./_Inline directory if you include CLEAN_AFTER_BUILD in the Config section of the script before building.

    Cheers,
    Rob
      OK, no problem. The xs code is as follows:
      #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include <rtl-sdr.h> typedef rtlsdr_dev_t * SDR__RTLSDR; MODULE = SDR::RTLSDR PACKAGE = SDR::RTLSDR PROTOTYPES: DISABLE const char * rtlsdr_get_device_name(index) uint32_t index int rtlsdr_get_device_usb_strings( index, manufact, product, serial) uint32_t index char *manufact char *product char *serial int rtlsdr_get_index_by_serial(serial) const char *serial int rtlsdr_open(dev, index) SDR::RTLSDR **dev uint32_t index int rtlsdr_close(dev) SDR::RTLSDR *dev int rtlsdr_set_xtal_freq(dev, rtl_freq, tuner_freq) SDR::RTLSDR *dev uint32_t rtl_freq uint32_t *tuner_freq int rtlsdr_get_xtal_freq(dev, rtl_freq, tuner_freq) SDR::RTLSDR *dev uint32_t *rtl_freq uint32_t *tuner_freq int rtlsdr_get_usb_strings(dev, manufact, product, serial) SDR::RTLSDR *dev char *manufact char *product char *serial int rtlsdr_write_eeprom(dev, data, offset, len) RTLSDRDevice_T *dev uint8_t *data uint8_t offset uint16_t len int rtlsdr_read_eeprom(dev, data, offset, len) SDR::RTLSDR *dev uint8_t *data uint8_t offset uint16_t len int rtlsdr_set_center_freq(dev, freq) SDR::RTLSDR *dev uint32_t freq uint32_t rtlsdr_get_center_freq(dev) SDR::RTLSDR *dev int rtlsdr_set_freq_correction(dev, ppm) SDR::RTLSDR *dev int ppm int rtlsdr_get_freq_correction(dev) SDR::RTLSDR *dev int rtlsdr_get_tuner_gains( dev, gains ) SDR::RTLSDR *dev int *gains int rtlsdr_set_tuner_gain(dev, gain) SDR::RTLSDR *dev int gain int rtlsdr_set_tuner_bandwidth(dev, bw) SDR::RTLSDR *dev uint32_t bw int rtlsdr_get_tuner_gain(dev) SDR::RTLSDR *dev int rtlsdr_set_tuner_if_gain(dev, stage, gain) SDR::RTLSDR *dev int stage int gain int rtlsdr_set_tuner_gain_mode(dev, manual) SDR::RTLSDR *dev int manual int rtlsdr_set_sample_rate(dev, rate) SDR::RTLSDR *dev uint32_t rate uint32_t rtlsdr_get_sample_rate(dev) SDR::RTLSDR *dev int rtlsdr_set_testmode(dev, on) SDR::RTLSDR *dev int on int rtlsdr_set_agc_mode(dev, on) SDR::RTLSDR *dev int on int rtlsdr_set_direct_sampling(dev, on) SDR::RTLSDR *dev int on int rtlsdr_set_offset_tuning(dev, on) SDR::RTLSDR *dev int on int rtlsdr_get_offset_tuning(dev) SDR::RTLSDR *dev int rtlsdr_reset_bufferrtl(dev) SDR::RTLSDR *dev int rtlsdr_read_sync(dev,buf,len, n_read); SDR::RTLSDR *dev void *buf int len int *n_read
      while the TYMAP file is:
      TYPEMAP RTLSDRDevice_T * T_PTROBJ SDR::RTLSDR T_PTROBJ const char * T_PV int * T_PV char * T_PV unsigned long int T_U_LONG uint8_t T_U_SHORT uint8_t * T_U_SHORT uint16_t T_U_SHORT uint32_t * T_U_SHORT uint32_t T_U_SHORT
      the library code is:
      #ifndef __RTL_SDR_H #define __RTL_SDR_H #ifdef __cplusplus extern "C" { #endif #include <stdint.h> #include <rtl-sdr_export.h> typedef struct rtlsdr_dev rtlsdr_dev_t; RTLSDR_API uint32_t rtlsdr_get_device_count(void); RTLSDR_API const char* rtlsdr_get_device_name(uint32_t index); /*! * Get USB device strings. * * NOTE: The string arguments must provide space for up to 256 bytes. * * \param index the device index * \param manufact manufacturer name, may be NULL * \param product product name, may be NULL * \param serial serial number, may be NULL * \return 0 on success */ RTLSDR_API int rtlsdr_get_device_usb_strings(uint32_t index, char *manufact, char *product, char *serial); /*! * Get device index by USB serial string descriptor. * * \param serial serial string of the device * \return device index of first device where the name matched * \return -1 if name is NULL * \return -2 if no devices were found at all * \return -3 if devices were found, but none with matching name */ RTLSDR_API int rtlsdr_get_index_by_serial(const char *serial); RTLSDR_API int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index); RTLSDR_API int rtlsdr_close(rtlsdr_dev_t *dev); /* configuration functions */ /*! * Set crystal oscillator frequencies used for the RTL2832 and the tun +er IC. * * Usually both ICs use the same clock. Changing the clock may make se +nse if * you are applying an external clock to the tuner or to compensate th +e * frequency (and samplerate) error caused by the original (cheap) cry +stal. * * NOTE: Call this function only if you fully understand the implicati +ons. * * \param dev the device handle given by rtlsdr_open() * \param rtl_freq frequency value used to clock the RTL2832 in Hz * \param tuner_freq frequency value used to clock the tuner IC in Hz * \return 0 on success */ RTLSDR_API int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_fr +eq, uint32_t tuner_freq);
      (this is only the first part, think that I need only the rtlsdr_open function, the other function before it works and return the correct output. Regards Edoardo M.
Re: help with XS pointers
by bliako (Monsignor) on Jul 31, 2020 at 11:32 UTC

    After you posted more code, I can see that:

    const char * rtlsdr_get_device_name(index) uint32_t index

    And so this:

    my $device = rtlsdr_get_device_name( $index );

    returns the device name as a string, given device index (integer), and not a pointer to a rtlsdr_dev_t structure as you assumed.

    Given:

    int rtlsdr_open(dev, index) SDR::RTLSDR **dev uint32_t index

    I am guessing that one must open a device by specifying the index. The function rtlsdr_open() should probably return the status as an integer and allocates internally the device, that's what I assume when it tells you to supply the double pointer for device: "Give me a memory location and I will do the internal decoration".

    Here is a stand-alone C example to demonstrate this pattern:

    #include <stdio.h> #include <malloc.h> typedef struct { int a; } s_t; int open_device(s_t **dev, int index); int main(void){ s_t *device = NULL; open_device(&device, 1); printf("A=%d\n", device->a); free(device); } int open_device(s_t **dev, int index){ *dev = (s_t *)malloc(sizeof(s_t)); (*dev)->a = 42; return 1; }

    How does that translate to Perl? Hmmm again a guess:

    my $serial_number = "00000001"; # from dmesg output my $index = rtlsdr_get_index_by_serial( $serial_number ); # it returns + 0, this is not an error value so I think is good my $name = rtlsdr_get_device_name( $index ); print "device name '$name'\n"; my $device = undef; # = SDL::...->new() ??? # i don't think so/how? my $status = rtlsdr_open( \$device , $index) print "status $status for index $index\n";

    cool project!

    bw, bliako

      Hi bliako, yes, you're right when assign the NULL value to the $device variable, yesterday I found a similar example from this site: http://sdr.f4gkr.org/trac/browser/gkSDR/Logiciel/rwhw/rtlsdr_iface.cpp?rev=9 which confirm also the idea of the status code (the correct return would be "0"). the initial idea was right but the return code is the same as before:
      device name 'Generic RTL2832U OEM' SDR::RTLSDR::rtlsdr_open: dev is not of type RTLSDRDevice_TPtrPtr at s +dr.pl line 13.
      Regards Edoardo M.

        Does that make sense at all?

        my $device = SDR::RTLSDR->new(); my $status = rtlsdr_open( \$device , $index); print "status $status for index $index\n";

        also see this example perlxstypemap (search for T_PTROBJ_SPECIAL) and the Netconfig example in perlxs

        Does the error message you cited corresponds for the typemap source code you have shown?

Re: help with XS pointers
by perlfan (Vicar) on Jul 31, 2020 at 01:35 UTC