Saturday 16 August 2014

Accessing hardware registers in userspace using memmap in Linux

Introduction:
If you want to access hardware registers in Linux userspace you can use the following example to explore access.  However if I recommend creating a Linux Device Driver if you want to do this properly.
Note that this is an advance Linux topic, so please do NOT try this first in a production system.

Example:
Full man can be found at
http://linux.die.net/man/3/mmap
Example:
/*
 * Maps real memory address to a virtual address.  The virtual address
 * then could be used to read/write the real memory as if it was
accessed
 * directly.
 * Adapted by Janaka Subhawickrama. Copyright 2007.
 * GPL software.
 */
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>

//Virtual address that is associated with the physical address
static char *regmap_addr = NULL;
//The following is the physical address of memory mapped registers of
the CPU
#define REGISTRY_MAP_ADDRESS_OFFSET        (0xE0000000)
#define REGISTRY_MAP_SIZE                (0x000FFFFF)
#define GPIO2_DATA_DIR_REG_OFFSET        (0x00000D00)
#define GPIO2_DATA_REG_OFFSET                (0x00000D08)
#define GPIO2_DONE_PIN                        (0x00800000)
#define GPIO2_FAULT_PIN                        (0x00400000)
#define GPIO2_PROGB_PIN                        (0x01000000)

int main(int argc, char *argv[])
{
        int devmem; // this is the "/dev/mem" descriptor
        //Registers of the CPU I am using is 32bit
        volatile unsigned int uiGet;
        volatile unsigned int *ptmp = NULL;
        //On embedded systems you may have to mknod /dev/mem
        printf("\nOpening /dev/mem");
        devmem = open("/dev/mem", O_RDWR | O_SYNC);
        if (devmem < 0)
        {
                printf("\nOpening of /dev/mem failed with (%d) %s\n", errno,
strerror(errno));
                return -1;
        }
        printf("\nMapping Memory mapped registers at %08X with size %08X",
REGISTRY_MAP_SIZE, REGISTRY_MAP_ADDRESS_OFFSET);
        regmap_addr = (char *)mmap( 0, REGISTRY_MAP_SIZE, PROT_READ|
PROT_WRITE, MAP_SHARED, devmem, REGISTRY_MAP_ADDRESS_OFFSET);
        if (regmap_addr == (char *)MAP_FAILED)
        {
                printf("\nCould not map registers (%d) %s", errno, strerror(errno));
                close(devmem);
                return -1;
        }
        //Now you can write to CPU registers as if you were from a boot
loader
        //Setup directions of GPIO pins
        *(volatile unsigned int *)(regmap_addr + GPIO2_DATA_DIR_REG_OFFSET) =
GPIO2_FAULT_PIN | GPIO2_PROGB_PIN;
        //Turn some GPIO Pins on
        *(volatile unsigned int *)(regmap_addr + GPIO2_DATA_REG_OFFSET) =
GPIO2_FAULT_PIN | GPIO2_PROGB_PIN;
        //Get some values of GPIO lines
        uiGet = *(volatile unsigned int *)(regmap_addr +
GPIO2_DATA_REG_OFFSET);
        printf("\nGPIO register %08X", uiGet);
        //Cleanup
        munmap((void *)REGISTRY_MAP_ADDRESS_OFFSET, REGISTRY_MAP_SIZE);
        close(devmem);
        return 0;
}

More comments and caveats can be found at:
https://groups.google.com/forum/#!msg/comp.os.linux.embedded/kOKNHeMGg58/pTY-jLMFu_gJ

No comments:

Post a Comment