diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b25c15b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/src/gpio.c b/src/gpio.c new file mode 100644 index 0000000..65df20f --- /dev/null +++ b/src/gpio.c @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include "log.h" + +#define GLINE_BUTTON1 17 +#define GLINE_BUTTON2 22 +#define GLINE_BUTTON3 23 +#define GLINE_BUTTON4 27 + +#define STATE_BUTTON1 (1 << 0) +#define STATE_BUTTON2 (1 << 1) +#define STATE_BUTTON3 (1 << 2) +#define STATE_BUTTON4 (1 << 3) + +#define INPUT 1 /* is really 0 for control register! */ +#define OUTPUT 0 /* is really 1 for control register! */ +#define ALT0 4 + +#define PUD_OFF 0 +#define PUD_DOWN 1 +#define PUD_UP 2 + +#define FSEL_OFFSET 0 +#define SET_OFFSET 7 +#define CLR_OFFSET 10 +#define PINLEVEL_OFFSET 13 +#define EVENT_DETECT_OFFSET 16 +#define RISING_ED_OFFSET 19 +#define FALLING_ED_OFFSET 22 +#define HIGH_DETECT_OFFSET 25 +#define LOW_DETECT_OFFSET 28 +#define PULLUPDN_OFFSET 37 +#define PULLUPDNCLK_OFFSET 38 + +#define PULLUPDN_OFFSET_2711_0 57 +#define PULLUPDN_OFFSET_2711_1 58 +#define PULLUPDN_OFFSET_2711_2 59 +#define PULLUPDN_OFFSET_2711_3 60 +#define PULLUPDN_2711_MAGIC 0x6770696F + +#define BLOCK_SIZE (4*1024) + +static int mem_fd = -1; +static volatile uint32_t *gpio_map = NULL; +static int is_2711 = 0; +static int last_state = 0; + +static void short_wait(void) +{ + int i; + + for (i=0; i<150; i++) + { /* wait 150 cycles */ + asm volatile("nop"); + } +} + +static int setup_the_memmap(void) +{ + if ((mem_fd = open("/dev/gpiomem", O_RDWR|O_SYNC)) > 0) + { + if ((gpio_map = (unit32_t *)mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, 0)) == MAP_FAILED) + { + gpio_map = NULL; + close(mem_fd); + log(LFATAL, "Unable to map /dev/gpiomem (%d)", errno); + return -1; + } + } + + /* ignore the rest of the possibilities for now */ + return 0; +} + +static void set_pullupdn(int gpio, int pud) +{ + if (is_2711) + { /* method for RPi 4 */ + int pullreg = PULLUPDN_OFFSET_2711_0 + (gpio >> 4); + int pullshift = (gpio & 0xF) << 1; + uint32_t pullbits; + unsigned pull = 0; + switch (pud) + { + case PUD_OFF: pull = 0; break; + case PUD_UP: pull = 1; break; + case PUD_DOWN: pull = 2; break; + default: pull = 0; /* keep as "off" */ + } + pullbits = *(gpio_map + pullreg); + pullbits &= ~(3 << pullshift); + pullbits |= (pull << pullshift); + *(gpio_map + pullreg) = pullbits; + } + else + { /* method for older models */ + 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; + } +} + +static void setup_gpio_line(int gpio, int direction, int pud) +{ + 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)); +} + +static int gpio_input(int gpio) +{ + int offset, value, mask; + + offset = PINLEVEL_OFFSET + (gpio / 32); + mask = (1 << gpio % 32); + value = *(gpio_map + offset) & mask; + return value; +} + +int Gpio_setup(void) +{ + if (setup_the_memmap() != 0) + return -1; + + is_2711 = (*(gpio_map + PULLUPDN_OFFSET_2711_3) != PULLUPDN_2711_MAGIC); + + setup_gpio_line(GLINE_BUTTON1, INPUT, PUD_UP); + setup_gpio_line(GLINE_BUTTON2, INPUT, PUD_UP); + setup_gpio_line(GLINE_BUTTON3, INPUT, PUD_UP); + setup_gpio_line(GLINE_BUTTON4, INPUT, PUD_UP); + /* TODO: other setup */ + + last_state = 0; + return 0; +} + +void Gpio_cleanup(void) +{ + /* TODO: additional cleanup may be required */ + + setup_gpio_line(GLINE_BUTTON1, INPUT, PUD_OFF); + setup_gpio_line(GLINE_BUTTON2, INPUT, PUD_OFF); + setup_gpio_line(GLINE_BUTTON3, INPUT, PUD_OFF); + setup_gpio_line(GLINE_BUTTON4, INPUT, PUD_OFF); + + if (gpio_map) + { + munmap(gpio_map, BLOCK_SIZE); + gpio_map = NULL; + } + if (mem_fd >= 0) + { + close(mem_fd); + mem_fd = -1; + } +} + +int Gpio_poll_buttons(void) +{ + int new_state = 0; + int tmp, ndx, mask; + + if (gpio_input(GLINE_BUTTON1) == 0) + new_state |= STATE_BUTTON1; + if (gpio_input(GLINE_BUTTON2) == 0) + new_state |= STATE_BUTTON2; + if (gpio_input(GLINE_BUTTON3) == 0) + new_state |= STATE_BUTTON3; + if (gpio_input(GLINE_BUTTON4) == 0) + new_state |= STATE_BUTTON4; + + if (last_state != new_state) + { + tmp = last_state & ~new_state; + for (ndx = 1, mask = 1; ndx <= 4; ndx++, mask <<= 1) + { + if (tmp & mask) + log(LDEBUG, "Button %d was released", ndx); + } + tmp = new_state & ~last_state; + for (ndx = 1, mask = 1; ndx <= 4; ndx++, mask <<= 1) + { + if (tmp & mask) + log(LDEBUG, "Button %d was pressed", ndx); + } + last_state = new_state; + } + return new_state; +} diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..3bfe8dc --- /dev/null +++ b/src/log.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include "log.h" + +static const char *severities[] = { "FATAL", "ERROR", "WARN ", "INFO ", "DEBUG" }; + +void log(int level, const char *format, ...) +{ + va_list argp; + struct timeval tv; + struct tm tm; + char timestamp[32]; + char buf[1024]; + + va_start(argp, format); + vsnprintf(buf, 1024, format, argp); + va_end(argp); + + gettimeofday(&tv); + localtime_r(&(tv.tv_sec), &tm) + strftime(timestamp, 32, "%F %T", &tm); + printf("%s.%06u %s %s\n", timestamp, tv.tv_usec, severities[level], buf); +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..c7eaaf2 --- /dev/null +++ b/src/log.h @@ -0,0 +1,12 @@ +#ifndef __LOG_H_INCLUDED +#define __LOG_H_INCLUDED + +#define LFATAL 0 +#define LERROR 1 +#define LWARN 2 +#define LINFO 3 +#define LDEBUG 4 + +extern void log(int level, const char *format, ...); + +#endif /* __LOG_H_INCLUDED */