Must call dwt_initialise() before dwt_checkidlerc() to avoid segfault?

The rx examples generally have this initialisation sequence:

  1. Set SPI rate.
  2. Power on/reset IC.
  3. dwt_probe()
  4. Poll dwt_checkidlerc()
  5. dwt_initialise()

See for example, simple_rx_nlos.c or rx_diagnostics.c.

In my experience (stm32) this reliably segfaults due to a read from a NULL pointer. And this seems entirely reasonable, since dwt_checkidlerc() calls this chain:

  • dwt_checkidlerc() in deca_compat.c
    • ioctl(DWT_CHECKIDLERC) in dw3000_device.c
      • ull_checkidlerc()
        • dwt_read16bitoffsetreg()
          • dwt_readfromdevice()
            • dwt_xfer3xxx(DW3000_SPI_RD_BIT)

And there, inside the DW3000_SPI_RD_BIT case, is this line:

if ((LOCAL_DATA(dw)->spicrc == DWT_SPI_CRC_MODE_WRRD) ...

LOCAL_DATA(dw) expands to dw->priv and priv is set to NULL in dwt_probe() when static_dw is assigned to dw. So this bombs out reliably.

The first thing dwt_initialise() does is malloc() this field, so calling it before dwt_checkidlerc() resolves the issue.

This seems pretty fundamental so I’m surprised I can find no discussion or explanation. Is my version of the API hosed? Am I using it wrong? Or is everyone quietly doing this as well?

Hello,

The most recent version of the driver is 08.02.02. If you have downloaded the most recent QM33 SDK 1.0.0, then you are using the latest software.

I reviewed the code in this SDK and it is similar to what you describe, dwt_checkidlerc is called before dwt_initialise().

dwt_initialise should be called only once the device has properly booted, and reach idle_rc stage.

The error is likely to be linked to an error in the STM32 porting, and incorrect allocation. I will raise it to the internal teams

1 Like

Looks like the same story in 08.02.02. For example, the application entry point in rx_diagnostics.c follows the probe-check-initialise ordering:

dwt_probe((struct dwt_probe_s *)&dw3000_probe_interf);

while (!dwt_checkidlerc()) /* Need to make sure DW IC is in IDLE_RC before proceeding */ { };

if (dwt_initialise(DWT_DW_INIT) == DWT_ERROR)

dw3000_probe_interf is defined in both the nRF52840 and STM_Nucleo projects in deca_probe_interface.c as:

const struct dwt_probe_s dw3000_probe_interf = 
{
    .dw = NULL,
    .spi = (void*)&dw3000_spi_fct,
    .wakeup_device_with_io = wakeup_device_with_io,
    .driver_list = (struct dwt_driver_s **)tmp_ptr,
    .dw_driver_num = 2,
};

That NULL dw gets set to &static_dw in dwt_probe(), as defined in deca_compat.c. static_dw is initialised to all zeros, and dwt_probe() only assigns a few fields - priv is not one of them.

So I can’t see how dw->priv can be anything other than NULL by the time dwt_checkidlerc() runs. I’m obviously missing something fundamental here, so I’m looking forward to your findings!