///////////////////////////////////////////////////////////////////////////////////////
// File GPIO.xs
// Description: XS module for HiPi::GPIO
// Copyright: Copyright (c) 2017 Mark Dootson
// License: This is free software; you can redistribute it and/or modify it under
// the same terms as the Perl 5 programming language system itself.
//
// Some of this work is based on pigpio - see
// https://github.com/joan2937/pigpio
///////////////////////////////////////////////////////////////////////////////////////
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "mylib/include/ppport.h"
#include <sys/mman.h>
#include <stdio.h>
#define GPIO_BASE 0x200000
#define GPIO_LEN 0xB4
#define PI_INIT_FAILED -1
#define BANK (gpio>>5)
#define BIT (1<<(gpio&0x1F))
/* gpio: 0-53 */
#define PI_MIN_GPIO 0
#define PI_MAX_GPIO 53
#define PI_PIN_ERROR -1
#define PI_EDGE_ERROR -1
/* user_gpio: 0-31 */
#define PI_MAX_USER_GPIO 31
/* level: 0-1 */
#define PI_OFF 0
#define PI_ON 1
#define PI_CLEAR 0
#define PI_SET 1
#define PI_LOW 0
#define PI_HIGH 1
/* mode: 0-7 */
#define PI_INPUT 0
#define PI_OUTPUT 1
#define PI_ALT0 4
#define PI_ALT1 5
#define PI_ALT2 6
#define PI_ALT3 7
#define PI_ALT4 3
#define PI_ALT5 2
#define PI_MAX_MODE 7
/* pud: 0-2 */
#define PI_PUD_OFF 0
#define PI_PUD_DOWN 1
#define PI_PUD_UP 2
/* locations */
#define GPFSEL0 0
#define GPSET0 7
#define GPSET1 8
#define GPCLR0 10
#define GPCLR1 11
#define GPLEV0 13
#define GPLEV1 14
#define GPEDS0 16
#define GPEDS1 17
#define GPREN0 19
#define GPREN1 20
#define GPFEN0 22
#define GPFEN1 23
#define GPHEN0 25
#define GPHEN1 26
#define GPLEN0 28
#define GPLEN1 29
#define GPAREN0 31
#define GPAREN1 32
#define GPAFEN0 34
#define GPAFEN1 35
#define GPPUD 37
#define GPPUDCLK0 38
#define GPPUDCLK1 39
static int fdMem = -1;
static volatile uint32_t * gpio_register = MAP_FAILED;
// pre-declares required
static int do_initialise(void);
static void do_uninitialise(void);
static void send_module_error( char * error );
// map a memory block
static uint32_t * map_gpiomem(int fd, uint32_t addr, uint32_t len)
{
return (uint32_t *) mmap(0, len, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_LOCKED, fd, addr);
}
// open /dev/gpiomem
static int open_gpiomen(void)
{
if ((fdMem = open("/dev/gpiomem", O_RDWR | O_SYNC) ) < 0)
{
return PI_INIT_FAILED;
}
return 0;
}
// initialise lib
static int do_initialise(void)
{
if(open_gpiomen() == PI_INIT_FAILED)
{
send_module_error("HiPi::GPIO failed to open memory device /dev/gpiomem");
do_uninitialise();
return 0;
}
gpio_register = map_gpiomem(fdMem, GPIO_BASE, GPIO_LEN);
if (gpio_register == MAP_FAILED)
{
send_module_error("HiPi::GPIO failed to map gpio memory block");
do_uninitialise();
return 0;
}
return 1;
}
// uninitialise lib
static void do_uninitialise(void)
{
if (gpio_register != MAP_FAILED) munmap((void *)gpio_register, GPIO_LEN);
gpio_register == MAP_FAILED;
if (fdMem != -1)
{
close(fdMem);
fdMem = -1;
}
}
// delay microseconds
static void delay_microseconds(uint32_t inputmicros)
{
struct timespec ts, rem;
int seconds = inputmicros / 1000000;
int micros = inputmicros % 1000000;
ts.tv_sec = seconds;
ts.tv_nsec = micros * 1000;
while ( clock_nanosleep(CLOCK_REALTIME, 0, &ts, &rem) )
{
ts = rem;
}
}
// set pud mode
static void do_gpio_set_pud(unsigned gpio, unsigned pud)
{
*(gpio_register + GPPUD) = pud;
delay_microseconds(20);
*(gpio_register + GPPUDCLK0 + BANK) = BIT;
delay_microseconds(20);
*(gpio_register + GPPUD) = 0;
*(gpio_register + GPPUDCLK0 + BANK) = 0;
}
// set pin mode
static void do_gpio_set_mode(unsigned gpio, unsigned mode)
{
int reg, shift;
reg = gpio/10;
shift = (gpio%10) * 3;
gpio_register[reg] = (gpio_register[reg] & ~(7<<shift)) | (mode<<shift);
}
// get pin mode
int do_gpio_get_mode(unsigned gpio)
{
int reg, shift;
reg = gpio/10;
shift = (gpio%10) * 3;
return (gpio_register[reg] >> shift) & 7;
}
// read pin value
static int do_gpio_read(unsigned gpio)
{
if ((*(gpio_register + GPLEV0 + BANK) & BIT) != 0)
{
return PI_ON;
} else {
return PI_OFF;
}
}
// write pin value
static void do_gpio_write(unsigned gpio, unsigned level)
{
if (level == PI_ON) {
*(gpio_register + GPSET0 + BANK) = BIT;
} else {
*(gpio_register + GPCLR0 + BANK) = BIT;
}
}
// set bits
static void do_gpio_set_bits( uint32_t offset, uint32_t value, uint32_t mask )
{
uint32_t val = *(gpio_register + offset);
val = (val & ~mask) | (value & mask);
*(gpio_register + offset) = val;
}
// edge detection register settings
static void do_gpio_set_egdereg_pin_bit(unsigned gpio, unsigned reg)
{
uint32_t value = BIT;
uint32_t mask = BIT;
uint32_t offset = reg + BANK;
do_gpio_set_bits( offset, value, mask );
}
static int do_gpio_get_egdereg_pin_bit(unsigned gpio, unsigned reg)
{
if ((*(gpio_register + reg + BANK) & BIT) != 0)
{
return PI_SET;
} else {
return PI_CLEAR;
}
}
static void do_gpio_clear_edgereg_pin_bit(unsigned gpio, unsigned reg)
{
uint32_t value = 0;
uint32_t mask = BIT;
uint32_t offset = reg + BANK;
do_gpio_set_bits( offset, value, mask );
}
static int do_gpio_get_eds(unsigned gpio)
{
if ((*(gpio_register + GPEDS0 + BANK) & BIT) != 0)
{
return PI_SET;
} else {
return PI_CLEAR;
}
}
static void do_gpio_set_eds(unsigned gpio)
{
*(gpio_register + GPEDS0 + BANK) = BIT;
}
// send error to module
static void send_module_error( char * error )
{
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 1);
PUSHs(sv_2mortal(newSVpv(error, 0)));
PUTBACK;
call_pv("HiPi::GPIO::error_report", G_DISCARD);
FREETMPS;
LEAVE;
}
MODULE = HiPi::GPIO PACKAGE = HiPi::GPIO
int
xs_initialise_gpio_block()
CODE:
RETVAL = do_initialise();
OUTPUT: RETVAL
void
xs_release_gpio_block()
CODE:
do_uninitialise();
int
xs_gpio_write( gpio, level )
unsigned gpio
unsigned level
CODE:
if (gpio > PI_MAX_GPIO) {
send_module_error("bad gpio number specified");
RETVAL = PI_PIN_ERROR;
} else if (level > PI_ON) {
send_module_error("bad level specified");
RETVAL = PI_PIN_ERROR;
} else {
// if (do_gpio_getmode(gpio) != PI_OUTPUT) do_gpio_setmode(gpio, PI_OUTPUT);
do_gpio_write( gpio, level );
RETVAL = (int)level;
}
OUTPUT: RETVAL
int
xs_gpio_read( gpio )
unsigned gpio
CODE:
if (gpio > PI_MAX_GPIO) {
send_module_error("bad gpio number specified");
RETVAL = PI_PIN_ERROR;
} else {
RETVAL = do_gpio_read(gpio);
}
OUTPUT: RETVAL
int
xs_gpio_set_mode( gpio, mode )
unsigned gpio
unsigned mode
CODE:
if (gpio > PI_MAX_GPIO) {
send_module_error("bad gpio number specified");
RETVAL = PI_PIN_ERROR;
} else if( mode > PI_MAX_MODE ){
send_module_error("bad mode specified");
RETVAL = PI_PIN_ERROR;
} else {
do_gpio_set_mode( gpio, mode);
RETVAL = (int)mode;
}
OUTPUT: RETVAL
int
xs_gpio_get_mode( gpio )
unsigned gpio
CODE:
if (gpio > PI_MAX_GPIO) {
send_module_error("bad gpio number specified");
RETVAL = PI_PIN_ERROR;
} else {
RETVAL = do_gpio_get_mode( gpio );
}
OUTPUT: RETVAL
int
xs_gpio_set_pud( gpio, pud )
unsigned gpio
unsigned pud
CODE:
if (gpio > PI_MAX_GPIO) {
send_module_error("bad gpio number specified");
RETVAL = PI_PIN_ERROR;
} else if( pud > PI_PUD_UP ){
send_module_error("bad pud action specified");
RETVAL = PI_PIN_ERROR;
} else {
// set the pud
do_gpio_set_pud( gpio, pud);
RETVAL = (int)pud;
}
OUTPUT: RETVAL
int xs_gpio_set_edge_detect( gpio, edge, onoff )
unsigned gpio
unsigned edge
unsigned onoff
CODE:
if (gpio > PI_MAX_GPIO) {
send_module_error("bad gpio number specified");
RETVAL = PI_PIN_ERROR;
} else if( edge != GPREN0 && edge != GPFEN0 && edge != GPHEN0 && edge != GPLEN0 && edge != GPAREN0 && edge != GPAFEN0 ) {
RETVAL = PI_EDGE_ERROR;
send_module_error("bad edge type specified");
} else if( onoff > 1 ){
send_module_error("bad edge setting specified");
RETVAL = PI_PIN_ERROR;
} else if( onoff == 1 ) {
// set the edge
do_gpio_set_egdereg_pin_bit( gpio, edge );
RETVAL = (int)gpio;
} else {
do_gpio_clear_edgereg_pin_bit( gpio, edge );
RETVAL = (int)gpio;
}
OUTPUT: RETVAL
int xs_gpio_get_edge_detect( gpio, edge )
unsigned gpio
unsigned edge
CODE:
if (gpio > PI_MAX_GPIO) {
send_module_error("bad gpio number specified");
RETVAL = PI_PIN_ERROR;
} else if( edge != GPREN0 && edge != GPFEN0 && edge != GPHEN0 && edge != GPLEN0 && edge != GPAREN0 && edge != GPAFEN0 ) {
RETVAL = PI_EDGE_ERROR;
send_module_error("bad edge type specified");
} else {
RETVAL = do_gpio_get_egdereg_pin_bit( gpio, edge );
}
OUTPUT: RETVAL
int
xs_gpio_clear_edge_detect( gpio )
unsigned gpio
CODE:
if (gpio > PI_MAX_GPIO) {
send_module_error("bad gpio number specified");
RETVAL = PI_PIN_ERROR;
} else {
do_gpio_set_eds( gpio );
RETVAL = (int)gpio;
}
OUTPUT: RETVAL
int
xs_gpio_read_edge_detect( gpio )
unsigned gpio
CODE:
if (gpio > PI_MAX_GPIO) {
send_module_error("bad gpio number specified");
RETVAL = PI_PIN_ERROR;
} else {
RETVAL = do_gpio_get_eds( gpio );
}
OUTPUT: RETVAL