/**
* @excerpts from my_test_gpio_interrupts.c
* as of 19 June 2020
*
* Demonstrates use sysFS to respond to
* gpio interrupts. Instructions are printed to the screen when called.
*/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h> // for atoi
#include <getopt.h>
#include <unistd.h> //usleep, nanosleep
#include <pthread.h> // read event thread
#include <poll.h> // interrupt events in read event thread
int running = (1==0);
int vehicleId =-1; //set based on model category in init_hw
int gpio_value_fd;
int gpio_num;
int rc_pthread_create(pthread_t *thread, void*(*func)(void*), void* arg, int policy, int priority);
float tspec2float(struct timespec tIn);
char *GP1_3_pinmux_path = "/sys/devices/platform/ocp/ocp:J15_pinmux/state";
char *GP1_4_pinmux_path = "/sys/devices/platform/ocp/ocp:H17_pinmux/state";
int GP1_3_num = 98; //GPIO3_2 is 3*32+2
int GP1_4_num = 97; //GPIO3_1 is 3*32+1
/**
* This is a list of general categories of boards.
*/
typedef enum rc_model_category_t{
CATEGORY_UNKNOWN,
CATEGORY_BEAGLEBONE,
CATEGORY_RPI,
CATEGORY_RPI3,
CATEGORY_PC
} rc_model_category_t;
rc_model_category_t rc_model_category(void);
char *model_category2string(void);
// printed if some invalid argument was given
static void __print_usage(void)
{
printf("\n");
switch (rc_model_category()){
case CATEGORY_BEAGLEBONE:
printf("-g GP1_pin Required. GP1.3 and GP1.4 are available. Enter 3 or 4.\n");
break;
case CATEGORY_RPI:
case CATEGORY_RPI3:
printf("-g GP1_pin Required. Enter 2 or 3.\n");
break;
default:
printf("-g gpio pin number Required. Varies by board type. Use caution.\n");
}
printf("-h print this help message\n");
printf("\n");
}
// interrupt handler to catch ctrl-c
void cleanup(int signo){
if (signo == SIGINT){
printf("received SIGINT Ctrl-C\n");
running = (1==0);
}
}
int main(int argc, char *argv[])
{
int fd;
int c, no_g=(1==1), len, ret, gpio;
char buf[80];
void* read_gpio_events(void* ptr);
pthread_t event_thread;
// parse arguments
opterr = 0;
while ((c = getopt(argc, argv, "g:h")) != -1){
switch (c){
case 'g': // gpio_pin
gpio = atoi(optarg);
switch(rc_model_category()){
case CATEGORY_BEAGLEBONE:
if (gpio == 3 || gpio == 4){
gpio_num = (gpio == 3?GP1_3_num:GP1_4_num);
no_g = (1==0);
}
else{
no_g = (1==1);
printf("GPIO pin on socket GP1 must be 3 or 4. Got %d\n",gpio_num);
}
break;
case CATEGORY_RPI:
case CATEGORY_RPI3:
if (gpio == 2 || gpio == 3){
gpio_num = gpio;
no_g = (1==0);
}
else{
no_g = (1==1);
printf("GPIO pin must be xx or 4xx. Got %d\n",gpio);
}
break;
default:
;//what to do???
}//category switch
break;
case 'h':
__print_usage();
return -1;
break;
default:
__print_usage();
return -1;
break;
}
}
// if the user didn't give enough arguments, print usage
if(no_g == (1==1)){
__print_usage();
return -1;
}
// set signal handler so the loop can exit cleanly
signal(SIGINT, cleanup);
running = (1==1);
// initialize hardware first
switch (rc_model_category()){
case CATEGORY_BEAGLEBONE:
//set up pinmux for gpio mode
fd = open((gpio == 3?GP1_3_pinmux_path:GP1_4_pinmux_path), O_WRONLY);
if (fd == -1)
{
printf("Error: open for path %s failed\n",(gpio == 3?GP1_3_pinmux_path:GP1_4_pinmux_path));
return -1;
}
ret = write(fd, "gpio_pu", 8);
if(ret<0){
printf("Error: write pinmux for path %s failed\n",(gpio == 3?GP1_3_pinmux_path:GP1_4_pinmux_path));
close(fd);
return -1;
}
close(fd);
//clean up left over open gpios by unexporting them
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (fd < 0) {
printf("Error: gpio/unexport close out failed\n");
return -1;
}
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d", gpio_num);
if(access(buf, F_OK)==0){
len = snprintf(buf, sizeof(buf), "%d", gpio_num );
write(fd, buf, len);
}
close(fd);
break;
case CATEGORY_RPI:
case CATEGORY_RPI3:
//clean up left over open gpios by unexporting them
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (fd < 0) {
printf("Error: gpio/unexport close out failed\n");
return -1;
}
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d", gpio_num);
if(access(buf, F_OK)==0){
len = snprintf(buf, sizeof(buf), "%d", gpio_num );
write(fd, buf, len);
}
close(fd);
break;
default:
;//no action proposed
}
//press on with exporting
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) {
printf("Error: gpio/export initialization failed\n");
return -1;
}
len = snprintf(buf, sizeof(buf), "%d", gpio_num);
ret = write(fd, buf, len);
if (ret == -1)
{
//gripe
}
close(fd);
//set direction to be in
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%i/direction", gpio_num);
fd = open(buf, O_WRONLY);
if (fd < 0) {
//gripe and cleanup via unexport
}
write(fd, "in", 3);
close(fd);
//set active_low to be false -> active high
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%i/active_low", gpio_num);
fd = open(buf, O_WRONLY);
if (fd < 0) {
//gripe and cleanup via unexport
}
write(fd, "0", 2);
close(fd);
//set edge interrupts for rising
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%i/edge", gpio_num);
fd = open(buf, O_WRONLY);
if (fd < 0) {
//gripe and cleanup via unexport
}
write(fd, "rising", 7);
close(fd);
//capture gpio value fd
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%i/value", gpio_num);
fd = open(buf, O_WRONLY);
if (fd < 0) {
//gripe and cleanup via unexport
}
gpio_value_fd=fd;
//do a first read to clear it
lseek(gpio_value_fd, 0, SEEK_SET);
read(gpio_value_fd, buf, sizeof(buf)/sizeof(char));
printf("initial clearing read of gpio value gave %s\n",buf);
//launch the thread to monitor the gpio events
//support routine elsewhere, "normal" scheduler and 0 priority
//ultimately will use SCHED_FIFO and a realtime priority something like 1--that also requires elevated privaledges
ret=rc_pthread_create(&event_thread, read_gpio_events, (void*) NULL,SCHED_OTHER,0);
if (ret != 0){
printf("Error %d (22 is EINVAL, 1 is EPERM): pthread_create for Blue read_encoder_events\n",ret);
}
else {
printf("hwLib: read_gpio_events thread created\n");
}
// wait until the user exits via ctrl-C
while(running == (1==1)){
usleep(500000);
}
// final cleanup--leave GPIOs set up if False
if(1==0){
printf("test_gpio_interrupts: closing gpios\n");
switch (rc_model_category()){
case CATEGORY_BEAGLEBONE:
//put pinmux back to default mode
fd = open((gpio == 3?GP1_3_pinmux_path:GP1_4_pinmux_path), O_WRONLY);
if (fd == -1)
{
//gripe
}
else {
ret = write(fd, "default", 8);
if(ret<0){
//gripe
}
close(fd);
}
break;
case CATEGORY_RPI:
case CATEGORY_RPI3:
//no action
break;
default:
;//no action
}
//clean up open gpios by unexporting them
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (fd < 0) {
//gripe
}
else {
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d", gpio_num);
if(access(buf, F_OK)==0){
len = snprintf(buf, sizeof(buf), "%d", gpio_num);
write(fd, buf, len);
}
close(fd);
}
}
else {
printf("test_gpio_interrupts: leaving gpios open\n");
}
printf("\ntest_gpio_interrupts: bye bye\n");
return 0;
}
#define POLL_TIMEOUT (1*1000) /* 1 seconds */
void* read_gpio_events(void *ptr){
struct pollfd fdset[1];
int ret, len;
char buf[80];
fdset[0].fd = gpio_value_fd;
fdset[0].events = (POLLPRI|POLLERR);
fdset[0].revents = 0;
//clearing read done by parent before thread launch
printf("gpio event thread is up\n");
while (running == (1==1))
{
//printf("calling poll( )\n");
fdset[0].revents = 0;
ret = poll(fdset, 1, POLL_TIMEOUT);
//printf("returning from poll( ) with return %d and event 0x%x\n",ret,fdset[0].revents);
if (ret == -1){
//error
printf("poll returned error\n");
return NULL;
}
if (ret == 0){
//timeout - go around again
printf("poll returned time out\n");
}
else {
if ((fdset[0].revents & POLLPRI) == POLLPRI) {
lseek(fdset[0].fd, 0, SEEK_SET);
len = read(gpio_value_fd, buf, sizeof(buf)/sizeof(char));
if (len < 1){
printf("gpio event = 0x%x, len = %d\n",fdset[0].revents,len);
}
else {
printf("gpio event = 0x%x, len = %d, value = %s\n",fdset[0].revents,len, buf);
}
}
}
}
printf("gpio event thread closing\n");
return NULL;
}