mirror of
https://github.com/mysensors/MySensors.git
synced 2026-03-06 08:14:51 +01:00
253 lines
6.2 KiB
C++
253 lines
6.2 KiB
C++
/*
|
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
|
* between your home built sensors/actuators and HA controller of choice.
|
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
|
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
|
|
* network topology allowing messages to be routed to nodes.
|
|
*
|
|
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
|
* Copyright (C) 2013-2017 Sensnology AB
|
|
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
|
|
*
|
|
* Documentation: http://www.mysensors.org
|
|
* Support Forum: http://forum.mysensors.org
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* version 2 as published by the Free Software Foundation.
|
|
*
|
|
* Based on wiringPi Copyright (c) 2012 Gordon Henderson.
|
|
*/
|
|
|
|
#include "interrupt.h"
|
|
#include <pthread.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ioctl.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <poll.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <stropts.h>
|
|
#include <errno.h>
|
|
#include <sched.h>
|
|
#include "log.h"
|
|
|
|
struct ThreadArgs {
|
|
void (*func)();
|
|
int gpioPin;
|
|
};
|
|
|
|
volatile bool interruptsEnabled = true;
|
|
static pthread_mutex_t intMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static pthread_t *threadIds[64] = {NULL};
|
|
|
|
// sysFds:
|
|
// Map a file descriptor from the /sys/class/gpio/gpioX/value
|
|
static int sysFds[64] = {
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
};
|
|
|
|
/*
|
|
* Part of wiringPi: Simple way to get your program running at high priority
|
|
* with realtime schedulling.
|
|
*/
|
|
int piHiPri(const int pri)
|
|
{
|
|
struct sched_param sched ;
|
|
|
|
memset (&sched, 0, sizeof(sched)) ;
|
|
|
|
if (pri > sched_get_priority_max (SCHED_RR)) {
|
|
sched.sched_priority = sched_get_priority_max (SCHED_RR) ;
|
|
} else {
|
|
sched.sched_priority = pri ;
|
|
}
|
|
|
|
return sched_setscheduler (0, SCHED_RR, &sched) ;
|
|
}
|
|
|
|
void *interruptHandler(void *args)
|
|
{
|
|
int fd;
|
|
struct pollfd polls;
|
|
char c;
|
|
struct ThreadArgs *arguments = (struct ThreadArgs *)args;
|
|
int gpioPin = arguments->gpioPin;
|
|
void (*func)() = arguments->func;
|
|
delete arguments;
|
|
|
|
(void)piHiPri(55); // Only effective if we run as root
|
|
|
|
if ((fd = sysFds[gpioPin]) == -1) {
|
|
logError("Failed to attach interrupt for pin %d\n", gpioPin);
|
|
return NULL;
|
|
}
|
|
|
|
// Setup poll structure
|
|
polls.fd = fd;
|
|
polls.events = POLLPRI | POLLERR;
|
|
|
|
while (1) {
|
|
// Wait for it ...
|
|
int ret = poll(&polls, 1, -1);
|
|
if (ret < 0) {
|
|
logError("Error waiting for interrupt: %s\n", strerror(errno));
|
|
break;
|
|
}
|
|
// Do a dummy read to clear the interrupt
|
|
// A one character read appars to be enough.
|
|
if (lseek (fd, 0, SEEK_SET) < 0) {
|
|
logError("Interrupt handler error: %s\n", strerror(errno));
|
|
break;
|
|
}
|
|
if (read (fd, &c, 1) < 0) {
|
|
logError("Interrupt handler error: %s\n", strerror(errno));
|
|
break;
|
|
}
|
|
// Call user function.
|
|
pthread_mutex_lock(&intMutex);
|
|
if (interruptsEnabled) {
|
|
pthread_mutex_unlock(&intMutex);
|
|
func();
|
|
} else {
|
|
pthread_mutex_unlock(&intMutex);
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void attachInterrupt(uint8_t gpioPin, void (*func)(), uint8_t mode)
|
|
{
|
|
FILE *fd;
|
|
char fName[40];
|
|
char c;
|
|
int count, i;
|
|
|
|
if (threadIds[gpioPin] == NULL) {
|
|
threadIds[gpioPin] = new pthread_t;
|
|
} else {
|
|
// Cancel the existing thread for that pin
|
|
pthread_cancel(*threadIds[gpioPin]);
|
|
// Wait a bit
|
|
usleep(1000);
|
|
}
|
|
|
|
// Export pin for interrupt
|
|
if ((fd = fopen("/sys/class/gpio/export", "w")) == NULL) {
|
|
logError("attachInterrupt: Unable to export pin %d for interrupt: %s\n", gpioPin, strerror(errno));
|
|
exit(1);
|
|
}
|
|
fprintf(fd, "%d\n", gpioPin);
|
|
fclose(fd);
|
|
|
|
// Wait a bit the system to create /sys/class/gpio/gpio<GPIO number>
|
|
usleep(1000);
|
|
|
|
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/direction", gpioPin) ;
|
|
if ((fd = fopen (fName, "w")) == NULL) {
|
|
logError("attachInterrupt: Unable to open GPIO direction interface for pin %d: %s\n",
|
|
gpioPin, strerror(errno));
|
|
exit(1) ;
|
|
}
|
|
fprintf(fd, "in\n") ;
|
|
fclose(fd) ;
|
|
|
|
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/edge", gpioPin) ;
|
|
if ((fd = fopen(fName, "w")) == NULL) {
|
|
logError("attachInterrupt: Unable to open GPIO edge interface for pin %d: %s\n", gpioPin,
|
|
strerror(errno));
|
|
exit(1) ;
|
|
}
|
|
|
|
switch (mode) {
|
|
case CHANGE:
|
|
fprintf(fd, "both\n");
|
|
break;
|
|
case FALLING:
|
|
fprintf(fd, "falling\n");
|
|
break;
|
|
case RISING:
|
|
fprintf(fd, "rising\n");
|
|
break;
|
|
case NONE:
|
|
fprintf(fd, "none\n");
|
|
break;
|
|
default:
|
|
logError("attachInterrupt: Invalid mode\n");
|
|
fclose(fd);
|
|
return;
|
|
}
|
|
fclose(fd);
|
|
|
|
if (sysFds[gpioPin] == -1) {
|
|
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/value", gpioPin);
|
|
if ((sysFds[gpioPin] = open(fName, O_RDWR)) < 0) {
|
|
logError("Error reading pin %d: %s\n", gpioPin, strerror(errno));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
// Clear any initial pending interrupt
|
|
ioctl(sysFds[gpioPin], FIONREAD, &count);
|
|
for (i = 0; i < count; ++i) {
|
|
if (read(sysFds[gpioPin], &c, 1) == -1) {
|
|
logError("attachInterrupt: failed to read pin status: %s\n", strerror(errno));
|
|
}
|
|
}
|
|
|
|
struct ThreadArgs *threadArgs = new struct ThreadArgs;
|
|
threadArgs->func = func;
|
|
threadArgs->gpioPin = gpioPin;
|
|
|
|
// Create a thread passing the pin and function
|
|
pthread_create(threadIds[gpioPin], NULL, interruptHandler, (void *)threadArgs);
|
|
}
|
|
|
|
void detachInterrupt(uint8_t gpioPin)
|
|
{
|
|
// Cancel the thread
|
|
if (threadIds[gpioPin] != NULL) {
|
|
pthread_cancel(*threadIds[gpioPin]);
|
|
delete threadIds[gpioPin];
|
|
threadIds[gpioPin] = NULL;
|
|
}
|
|
|
|
// Close filehandle
|
|
if (sysFds[gpioPin] != -1) {
|
|
close(sysFds[gpioPin]);
|
|
sysFds[gpioPin] = -1;
|
|
}
|
|
|
|
FILE *fp = fopen("/sys/class/gpio/unexport", "w");
|
|
if (fp == NULL) {
|
|
logError("Unable to unexport pin %d for interrupt\n", gpioPin);
|
|
exit(1);
|
|
}
|
|
fprintf(fp, "%d", gpioPin);
|
|
fclose(fp);
|
|
}
|
|
|
|
void interrupts()
|
|
{
|
|
pthread_mutex_lock(&intMutex);
|
|
interruptsEnabled = true;
|
|
pthread_mutex_unlock(&intMutex);
|
|
}
|
|
|
|
void noInterrupts()
|
|
{
|
|
pthread_mutex_lock(&intMutex);
|
|
interruptsEnabled = false;
|
|
pthread_mutex_unlock(&intMutex);
|
|
}
|