l Steveo l
l Steveo l

Reputation: 558

mmap dies with cannot open /dev/mem after several accesses

I am writing some user-space drivers using mmap on a dual core ARM A9 to access some HW registers inside an FPGA. We have a custom SPI block that talks to another chip which we access HW registers inside that chip. I wrote a C++ class for my SPI "Driver" to basically break down reads/writes from/to addresses inside the external chip. I use mmap to access the SPI that is inside the FPGA (only occupies about 100bytes of memory space (0xFF20_2000 -> 0xFF20_007F)).

When I call the SPI class (and I only do this once for the driver) it goes through and performs a mmap open for the base address of the SPI HW.

void spi_op::cfg_mmap(){

    if( ( this->fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) {
        printf( "ERROR: could not open \"/dev/mem\"...\n" );
        this->error = 1;
        return;
    }

    uint32_t map_addr_base = this->addr_base & MMAP_BASE_MASK;

    this->virtual_base = mmap( NULL, MMAP_PAGE_SIZE, ( PROT_READ | PROT_WRITE ), MAP_SHARED, this->fd, map_addr_base );

    if( this->virtual_base == MAP_FAILED ) {
        printf( "ERROR: mmap() failed...\n" );
        close( this->fd );
        this->error = 1;
        return;
    }

    //To initialize to the proper pointer in the event not on a page boundary
    this->virtual_base += (SPI_MASTER_ADDR_BASE - map_addr_base);
}

I have the SPI HW registers built as a struct/union for easy(ier) bit manipulations (I've X'd out things that could be considered proprietary, but are irrelevant for the question).

union spi_regs_reg1 {
    struct {
        volatile uint32_t xxxx: 32;
    } bits;
    volatile uint32_t word;
};

union spi_regs_reg2 {
    struct {
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
        volatile uint32_t xxxx: 1;
    } bits;
    volatile uint32_t word;
};

struct spi_regs_regs {
    union spi_regs_blockid spi_regs_reg1;
    union spi_regs_gnrlsta spi_regs_reg2;
    blah blah blah

As part of the init/constructor I have a spi_regs struct pointer that gets assigned

void spi_op::spi_master_init(){

    this->cfg_mmap();

    spi_regs = (struct spi_regs_regs *)(this->virtual_base);


    //an Example of how I can now access the bits in the HW registers
    //Assert the RESET
    spi_regs->spi_regs_reg.bits.spi_reset = 1;

Whenever I want to do a read or write, I now simply call spi.read(addr) or spi.write(addr, data) to perform the operation.

This seems to work just fine, but occasionally after a bunch of read/writes I begin to get errors

"ERROR: could not open "/dev/mem"...". 

Now this is something that is in the cfg_mmap() task. However I only call this once as part of the constructor. In this particular test I am setting up multiple different settings and running through each of them and resetting the external part inbetween (but not the SPI that I'm controlling with the mmap). I essentially have a for loop but the constructor of the SPI class is PRIOR to the for loop.

As a quick test while I was typing this, I change the error message in my cfg_mmap task to

"ERROR: blah could not open "/dev/mem"..."

then during the run I get this

<normal prints from my code>
ERROR: could not open "/dev/mem"...
ERROR: could not open "/dev/mem"...
ERROR: could not open "/dev/mem"...
ERROR: could not open "/dev/mem"...
ERROR: could not open "/dev/mem"...
ERROR: blah could not open "/dev/mem"...
Segmentation fault
root@arria10:~# 

Is it possible that /dev/mem is getting overloaded or something like that? I've done some scouring in my code, I can't for the life of me find where I'm calling the SPI class more than once (the program isn't that complicated).

Upvotes: 0

Views: 1079

Answers (1)

0x0C4
0x0C4

Reputation: 205

If I assume that you class is called "spi_op" you may need to create spi_op::~spi_op() (so called "destructor") with a "munmap(virtual_base,MMAP_PAGE_SIZE)" and "close(fd)" instruction. So it may be a topic of destructor : see https://www.tutorialspoint.com/cplusplus/cpp_constructor_destructor.htm for example. If you proper use munmap() and close() you should not run into this problem anymore.

Upvotes: 1

Related Questions