mmapでのGPIO設定

mmapによるユーザランドからのGPIO制御。
デバイスドライバによる方法よりオーバヘッドが少ない。

/* gpioMmap.h */


// definition
typedef enum{
  E_GPFSEL_INPUT = 0,
  E_GPFSEL_OUTPUT = 1,
  E_GPFSEL_F0 = 4,
  E_GPFSEL_F1 = 5,
  E_GPFSEL_F2 = 6,
  E_GPFSEL_F3 = 7,
  E_GPFSEL_F4 = 3,
  E_GPFSEL_F5 = 2
} E_GPFSEL;


// finction prototypes
void gpio_open(void);
void gpio_close(void);
void set_gpio_function(int pin, E_GPFSEL function);
void set_gpio(int pin, int level);
int get_gpio(int pin);
// gpioMmap.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

#include "gpioMmap.h"

//----------
// Macros
#define   PRINTLINE printf("%s:%d\n", __FUNCTION__, __LINE__)

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)
#define GPIO_BASE 0x20200000

//----------
// global variables
int mem_fd;           // mmap file
void *ptr;            // virtual memory allocated by alloc

// the variable gpio points to 0x20200000, that is GPIO registers
// starts from GPFSEL0 to GPPUDCLK1
volatile unsigned long *gpio;    // 0x20200000
volatile unsigned long *gpfsel;  // 0x20200000
volatile unsigned long *gpset;   // 0x2020001C
volatile unsigned long *gpclr;   // 0x20200028
volatile unsigned long *gplev;   // 0x20200034


/*-----------------------------------------------
 * Open the GPIO Lib
 */
void gpio_open(void)
{
  char *gpio_map;
  char *gpio_mem;

  PRINTLINE;

  // open /dev/mem
  mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
  if(mem_fd < 0){
    perror("Fail to open /dev/mem");
    exit(-1);
  }

  ptr = malloc(BLOCK_SIZE + (PAGE_SIZE - 1));
  if(ptr == NULL){
    perror("Fail to malloc");
    exit(-1);
  }

  // align page boundary
  if((unsigned long)ptr % PAGE_SIZE != 0){
    gpio_mem = ptr + (PAGE_SIZE - ((unsigned long)ptr % PAGE_SIZE));
  }
  else{
    gpio_mem = ptr;
  }

  // map virtual to physical
  gpio_map = (char *)mmap(
			  (caddr_t)gpio_mem,
			  BLOCK_SIZE,
			  PROT_READ | PROT_WRITE,
			  MAP_SHARED | MAP_FIXED,
	       		  mem_fd,
      			  GPIO_BASE
			  );
  if((long)gpio_map < 0){
    perror("Fail to mmap");
    exit(-1);
  }

  gpio = (volatile unsigned long *)gpio_map;

  gpfsel = gpio + (0x00/4);
  gpset  = gpio + (0x1C/4);
  gpclr  = gpio + (0x28/4);
  gplev  = gpio + (0x34/4);

}



/*-----------------------------------------------
 * Close the GPIO Lib
 */
void gpio_close(void)
{
  close(mem_fd);
  free(ptr);
}


/*-----------------------------------------------
 * set function to the GPIO pin
 * once clear the 3 bits then set the function to the 3 bits
 */
void set_gpio_function(int pin, E_GPFSEL function)
{
  volatile unsigned long *ptr;

  ptr = (gpfsel + (pin / 10));
  *ptr &= ~(7 << ((pin % 10) * 3));
  *ptr |= (function << ((pin % 10) * 3));
}


/*-----------------------------------------------
 * set the GPIO pin to High/Low
 */
void set_gpio(int pin, int level)
{
  volatile unsigned long *ptr;

  if(level == 1) ptr = gpset;
  else           ptr = gpclr;

  if(pin < 32){ ptr = ptr + 0; }
  else{         ptr = ptr + 1;  pin -= 32; }
  *ptr = 1 << pin;
}


/*-----------------------------------------------
 * get the level of the GPIO pin
 */
int get_gpio(int pin)
{
  volatile unsigned long *gp;

  if(pin < 32){ gp = gplev + 0; }
  else{         gp = gplev + 1;  pin -= 32; }

  return (((*gp) >> pin) & 1);
}