ioctl error: bad address (but only sometimes)

Viewed 68

So I'm using SPI to communicate the IMX8MQ-var-dart with an FPGA. The idea is that I send a command to the FPGA and this should answer me with a reading of an ADC connected to it.

My problem is that sometimes the communication works correctly but other times the ioctl command gives me the error "Bad address" when using the SPI.

I'm using this function for communicating with the FPGA:

int transfer16(int fd, uint16_t *tx, uint16_t *rx, uint32_t len){
int ret;

struct spi_ioc_transfer tr = {
    .tx_buf = (unsigned long)tx,
    .rx_buf = (unsigned long)rx,
    .len = len,
    .delay_usecs = 1,
    .speed_hz = spi_speed,
    .bits_per_word = 16,
};

ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if(ret<0){
    printf("Error: %s\n", strerror(errno));
}

return ret;

}
[...]

This function is called by the following function:

int send_command_readadc(int fd, int16_t *rx, uint16_t ndata_adc, uint8_t membank) {

int ret;

uint16_t tx[2];
uint16_t crc16_o, crc16_i;
uint8_t rcommand;
uint8_t ack;
uint32_t len = 2*(ndata_adc+NUMELS(tx)+2); // 2*sizeof(rx)
int i = 0;

tx[0] = (THE_READADC_COMMAND << 8) + membank;
tx[1] = crc16_uint16_false(tx,NUMELS(tx)-1);

ret = transfer16(fd, tx, rx, len);

if (ret==-1) {
    return -1;
}

And I call this function using the following parameters:

ret = send_command_readadc(fd_spi, (int16_t *)(data_adc+(ndata_adc+4)*i), ndata_adc, membank);

Where data_adc is a pointer to a allocated part of memory where I want to save the ADC readings:

data_adc = (int16_t *) calloc((ndata_adc+4)*M,sizeof(int16_t));

So, for example, if M is 3 I save the quantity of (ndata_adc+4) starting from the register data_adc. Then (ndata_adc+4) starting from the register data_adc+(ndata_adc+4) etc.

If I execute the program, it shows the following results:

root@imx8mq-var-dart:~# ./myspi.elf -a 3                                        
Niceness value set from 0 to -15                                                
spi mode: 0x2                                                                   
bits per word: 16                                                               
max speed: 11000000 Hz (11000 KHz)                                              
Number of ADC Channels Enabled: 2                                               
                        Test enchan_adc = 03 Passed!!                           
root@imx8mq-var-dart:~# ./myspi.elf -a 3                                        
Niceness value set from 0 to -15                                                
spi mode: 0x2                                                                   
bits per word: 16                                                               
max speed: 11000000 Hz (11000 KHz)                                              
Number of ADC Channels Enabled: 2                                               
Error: Bad address                                                              
        Error in READADC transfer16()                                           
Error: Bad address                                                              
        Error in READADC transfer16()                                           
Error: Bad address                                                              
        Error in READADC transfer16()                                           
                        Test enchan_adc = 03 Not Passed!!  

The error occurs randomly, so I don't know what is causing it. I hope this is enough explanation to the problem. If not, ask for more information without hesitation.

1 Answers

"Bad address" is EFAULT errno. This is typically returned by the Linux device drivers when the supplied user space addresses are erroneous. That is to say, the underlying driver got an error from a copy_to/from_user() call when transferring data to/from user space.

I looked at the source code of the SPI driver. It seems that the issue could come from the copy_to_user() in the spidev_message() function called by the ioctl() entry point. So, it would mean that there is an issue with the rx buffer that you pass to the ioctl(). Are you sure that it is always set with a correct value? Add a check in transfer16() function to verify that it is at least not NULL and correctly aligned.

Related