Showing posts with label Hardware registers. Show all posts
Showing posts with label Hardware registers. Show all posts

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