/* GPIO Extension Module
UXLIBS: -lc
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "c_gpio.h"
#include "../../basext.h"
#define BCM2708_PERI_BASE_DEFAULT 0x20000000
#define BCM2709_PERI_BASE_DEFAULT 0x3f000000
#define GPIO_BASE_OFFSET 0x200000
#define FSEL_OFFSET 0 // 0x0000
#define SET_OFFSET 7 // 0x001c / 4
#define CLR_OFFSET 10 // 0x0028 / 4
#define PINLEVEL_OFFSET 13 // 0x0034 / 4
#define EVENT_DETECT_OFFSET 16 // 0x0040 / 4
#define RISING_ED_OFFSET 19 // 0x004c / 4
#define FALLING_ED_OFFSET 22 // 0x0058 / 4
#define HIGH_DETECT_OFFSET 25 // 0x0064 / 4
#define LOW_DETECT_OFFSET 28 // 0x0070 / 4
#define PULLUPDN_OFFSET 37 // 0x0094 / 4
#define PULLUPDNCLK_OFFSET 38 // 0x0098 / 4
#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)
static volatile uint32_t *gpio_map;
static void short_wait(void)
{
int i;
for (i=0; i<150; i++) { // wait 150 cycles
asm volatile("nop");
}
}
static void clear_event_detect(int gpio)
{
int offset = EVENT_DETECT_OFFSET + (gpio/32);
int shift = (gpio%32);
*(gpio_map+offset) |= (1 << shift);
short_wait();
*(gpio_map+offset) = 0;
}
static void set_pullupdn(int gpio, int pud)
{
int clk_offset = PULLUPDNCLK_OFFSET + (gpio/32);
int shift = (gpio%32);
if (pud == PUD_DOWN)
*(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_DOWN;
else if (pud == PUD_UP)
*(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_UP;
else // pud == PUD_OFF
*(gpio_map+PULLUPDN_OFFSET) &= ~3;
short_wait();
*(gpio_map+clk_offset) = 1 << shift;
short_wait();
*(gpio_map+PULLUPDN_OFFSET) &= ~3;
*(gpio_map+clk_offset) = 0;
}
/**************************
Extension Module Functions
**************************/
typedef struct _ModuleObject {
void *HandleArray;
}ModuleObject,*pModuleObject;
besVERSION_NEGOTIATE
return (int)INTERFACE_VERSION;
besEND
besSUB_START
pModuleObject p;
besMODULEPOINTER = besALLOC(sizeof(ModuleObject));
if( besMODULEPOINTER == NULL )return 0;
p = (pModuleObject)besMODULEPOINTER;
return 0;
besEND
besSUB_FINISH
pModuleObject p;
p = (pModuleObject)besMODULEPOINTER;
if( p == NULL )return 0;
return 0;
besEND
/***************
GPIO Functions
***************/
besFUNCTION(sb_short_wait)
int i;
for (i=0; i<150; i++) { // wait 150 cycles
asm volatile("nop");
}
besEND
besFUNCTION(sb_setup)
int mem_fd;
uint8_t *gpio_mem;
uint32_t peri_base = 0;
uint32_t gpio_base;
unsigned char buf[4];
FILE *fp;
char buffer[1024];
char hardware[1024];
int found = 0;
// try /dev/gpiomem first - this does not require root privs
if ((mem_fd = open("/dev/gpiomem", O_RDWR|O_SYNC)) > 0)
{
if ((gpio_map = (uint32_t *)mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, 0)) == MAP_FAILED) {
besRETURN_LONG(SETUP_MMAP_FAIL);
} else {
besRETURN_LONG(SETUP_OK);
}
}
// revert to /dev/mem method - requires root
// determine peri_base
if ((fp
= fopen("/proc/device-tree/soc/ranges", "rb")) != NULL
) { // get peri base from device tree
if (fread(buf
, 1, sizeof buf
, fp
) == sizeof buf
) { peri_base = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0;
}
} else {
// guess peri base based on /proc/cpuinfo hardware field
if ((fp
= fopen("/proc/cpuinfo", "r")) == NULL
) besRETURN_LONG(SETUP_CPUINFO_FAIL);
while(!feof(fp
) && !found
&& fgets(buffer
, sizeof(buffer
), fp
)) { sscanf(buffer
, "Hardware : %s", hardware
); if (strcmp(hardware
, "BCM2708") == 0 || strcmp(hardware
, "BCM2835") == 0) { // pi 1 hardware
peri_base = BCM2708_PERI_BASE_DEFAULT;
found = 1;
} else if (strcmp(hardware
, "BCM2709") == 0 || strcmp(hardware
, "BCM2836") == 0) { // pi 2 hardware
peri_base = BCM2709_PERI_BASE_DEFAULT;
found = 1;
}
}
if (!found)
besRETURN_LONG(SETUP_NOT_RPI_FAIL);
}
if (!peri_base)
besRETURN_LONG(SETUP_NOT_RPI_FAIL);
gpio_base = peri_base + GPIO_BASE_OFFSET;
// mmap the GPIO memory registers
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)
besRETURN_LONG(SETUP_DEVMEM_FAIL);
if ((gpio_mem
= malloc(BLOCK_SIZE
+ (PAGE_SIZE
-1))) == NULL
) besRETURN_LONG(SETUP_MALLOC_FAIL);
if ((uint32_t)gpio_mem % PAGE_SIZE)
gpio_mem += PAGE_SIZE - ((uint32_t)gpio_mem % PAGE_SIZE);
if ((gpio_map = (uint32_t *)mmap( (void *)gpio_mem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, gpio_base)) == MAP_FAILED)
besRETURN_LONG(SETUP_MMAP_FAIL);
besRETURN_LONG(SETUP_OK);
besEND
besFUNCTION(sb_clear_event_detect)
int gpio;
besARGUMENTS("i")
&gpio
besARGEND
int offset = EVENT_DETECT_OFFSET + (gpio/32);
int shift = (gpio%32);
*(gpio_map+offset) |= (1 << shift);
short_wait();
*(gpio_map+offset) = 0;
besEND
besFUNCTION(sb_eventdetected)
int gpio;
besARGUMENTS("i")
&gpio
besARGEND
int offset, value, bit;
offset = EVENT_DETECT_OFFSET + (gpio/32);
bit = (1 << (gpio%32));
value = *(gpio_map+offset) & bit;
if (value)
clear_event_detect(gpio);
besRETURN_LONG(value);
besEND
besFUNCTION(sb_set_rising_event)
int gpio, enable;
besARGUMENTS("ii")
&gpio, &enable
besARGEND
int offset = RISING_ED_OFFSET + (gpio/32);
int shift = (gpio%32);
if (enable)
*(gpio_map+offset) |= 1 << shift;
else
*(gpio_map+offset) &= ~(1 << shift);
clear_event_detect(gpio);
besEND
besFUNCTION(sb_set_falling_event)
int gpio, enable;
besARGUMENTS("ii")
&gpio, &enable
besARGEND
int offset = FALLING_ED_OFFSET + (gpio/32);
int shift = (gpio%32);
if (enable) {
*(gpio_map+offset) |= (1 << shift);
*(gpio_map+offset) = (1 << shift);
} else {
*(gpio_map+offset) &= ~(1 << shift);
}
clear_event_detect(gpio);
besEND
besFUNCTION(sb_set_high_event)
int gpio, enable;
besARGUMENTS("ii")
&gpio, &enable
besARGEND
int offset = HIGH_DETECT_OFFSET + (gpio/32);
int shift = (gpio%32);
if (enable)
*(gpio_map+offset) |= (1 << shift);
else
*(gpio_map+offset) &= ~(1 << shift);
clear_event_detect(gpio);
besEND
besFUNCTION(sb_set_low_event)
int gpio, enable;
besARGUMENTS("ii")
&gpio, &enable
besARGEND
int offset = LOW_DETECT_OFFSET + (gpio/32);
int shift = (gpio%32);
if (enable)
*(gpio_map+offset) |= 1 << shift;
else
*(gpio_map+offset) &= ~(1 << shift);
clear_event_detect(gpio);
besEND
besFUNCTION(sb_set_pullupdn)
int gpio, pud;
besARGUMENTS("ii")
&gpio, &pud
besARGEND
int clk_offset = PULLUPDNCLK_OFFSET + (gpio/32);
int shift = (gpio%32);
if (pud == PUD_DOWN)
*(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_DOWN;
else if (pud == PUD_UP)
*(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_UP;
else // pud == PUD_OFF
*(gpio_map+PULLUPDN_OFFSET) &= ~3;
short_wait();
*(gpio_map+clk_offset) = 1 << shift;
short_wait();
*(gpio_map+PULLUPDN_OFFSET) &= ~3;
*(gpio_map+clk_offset) = 0;
besEND
besFUNCTION(sb_setup_gpio)
int gpio, direction, pud;
besARGUMENTS("iii")
&gpio, &direction, &pud
besARGEND
int offset = FSEL_OFFSET + (gpio/10);
int shift = (gpio%10)*3;
set_pullupdn(gpio, pud);
if (direction == OUTPUT)
*(gpio_map+offset) = (*(gpio_map+offset) & ~(7<<shift)) | (1<<shift);
else // direction == INPUT
*(gpio_map+offset) = (*(gpio_map+offset) & ~(7<<shift));
besEND
besFUNCTION(sb_gpio_function)
int gpio;
besARGUMENTS("i")
&gpio
besARGEND
int offset = FSEL_OFFSET + (gpio/10);
int shift = (gpio%10)*3;
int value = *(gpio_map+offset);
value >>= shift;
value &= 7;
besRETURN_LONG(value); // 0=input, 1=output, 4=alt0
besEND
besFUNCTION(sb_output_gpio)
int gpio, value;
besARGUMENTS("ii")
&gpio, &value
besARGEND
int offset, shift;
if (value) // value == HIGH
offset = SET_OFFSET + (gpio/32);
else // value == LOW
offset = CLR_OFFSET + (gpio/32);
shift = (gpio%32);
*(gpio_map+offset) = 1 << shift;
besEND
besFUNCTION(sb_input_gpio)
int gpio;
besARGUMENTS("i")
&gpio
besARGEND
int offset, value, mask;
offset = PINLEVEL_OFFSET + (gpio/32);
mask = (1 << gpio%32);
value = *(gpio_map+offset) & mask;
besRETURN_LONG(value);
besEND
besFUNCTION(sb_cleanup)
munmap((void *)gpio_map, BLOCK_SIZE);
besEND