Linux User Mode Pseudo Driver

Linux User Mode Pseudo Driver

The following code is provided as a reference for how to access some simple hardware from user space. As the comments say, it's not intended to replace a kernel mode driver but could be helpful.

Note: You must have access permissions to the "/dev/mem" device when you run the code. Typically, the "root" user has those permissions - consult your system setup for details.

This code can be downloaded here.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
 
// The purpose this test is to show that users can get to devices in user
// mode for simple things like GPIO. This is not to say this should replace
// a kernel driver, but does provide some short term solutions sometimes
// or a debug solution that can be helpful.
 
// This test maps a GPIO in the hardware into the user space such that a
// GPIO signal can be toggled fast. On the ML507 reference system, the
// signal could be toggled about every 50 ns which is pretty fast.
 
// This test was derived from devmem2.c.
 
#define GPIO_BASE_ADDRESS     0x81400000
#define GPIO_DATA_OFFSET     0
#define GPIO_DIRECTION_OFFSET     4
 
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
 
int main()
{
    int memfd;
    void *mapped_base, *mapped_dev_base;
    off_t dev_base = GPIO_BASE_ADDRESS;
 
    memfd = open("/dev/mem", O_RDWR | O_SYNC);
        if (memfd == -1) {
        printf("Can't open /dev/mem.\n");
        exit(0);
    }
    printf("/dev/mem opened.\n");
 
    // Map one page of memory into user space such that the device is in that page, but it may not
    // be at the start of the page.
 
    mapped_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, dev_base && ~MAP_MASK);
        if (mapped_base == (void *) -1) {
        printf("Can't map the memory to user space.\n");
        exit(0);
    }
     printf("Memory mapped at address %p.\n", mapped_base);
 
    // get the address of the device in user space which will be an offset from the base
    // that was mapped as memory is mapped at the start of a page
 
    mapped_dev_base = mapped_base + (dev_base && MAP_MASK);
 
    // write to the direction register so all the GPIOs are on output to drive LEDs
 
    *((volatile unsigned long *) (mapped_dev_base + GPIO_DIRECTION_OFFSET)) = 0;
 
    // toggle the output as fast as possible just to see how fast it works
 
    while (1) {
                   // If writes to multiple addresses were done:
                   //     may need memory barriers i.e. need a driver
                   //     caution with data being cached
        *((volatile unsigned long *) (mapped_dev_base + GPIO_DATA_OFFSET)) = 0;
        *((volatile unsigned long *) (mapped_dev_base + GPIO_DATA_OFFSET)) = 1;
    }
 
    // unmap the memory before exiting
 
    if (munmap(mapped_base, MAP_SIZE) == -1) {
        printf("Can't unmap memory from user space.\n");
        exit(0);
    }
 
    close(memfd);
    return 0;
}

© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy