Files
MySensors/drivers/Linux/interrupt.cpp
T
Olivier 8bb3cfb2e1 MySensors 2.1.1 release (#752)
* Bump version to 2.2.0-beta (#713)

* OTA: Add incoming FW block check (#718)

* Linux: Add support for SPIDEV (#734)

RPi and Linux refactor.

Add GPIO Sysfs for GPIO operations.

Update configure script.

Fix some cppcheck warnings.

Add serial emulation that prints to stdout.

Fix some file headers.

* Fix getControllerConfig() (#737)

* Rename MyHwATmega328 to MyHwAVR (#738)

* AVR: Restrict fast pin function use (#739)

* Fix signing presentation bug (#740)

* Consolidate open PRs for bugfix release (#741)

* Revert "Bump version to 2.2.0-beta" (#744)

* Merge Master into Dev (#745)

* Fix spi flash error when compiling in Arduino IDE 1.6.6

* Bump minor version

* Fixed "invalid suffix on literal" warning

* Fix addressing bug in _doSign bitfield

* Make sure nodes not supporting signing informs GW

A node that does not support signing, still needs to inform the
gateway about this to make sure the gateway carries a valid signing
requirement table when a node Id that was requiering signing stops
doing this.
This fixes #286.

* Prevent SecureActuator from accepting ACKs as commands

As ACKs are not currently signed, allowing ACKs as commands
is a considerable security hole for this sketch. This is now resolved.

* Backport of bugfix (#259) in repeaters when forwarding signed messages

* Bumped version to 1.5.2

* Corrected doSign variable name (was _doSign)

* Bumped version to 1.5.3

* Add NULL termination to payloads

Messages addressed to "this" node, will have a null char
appended to the payload after message verification is
done, because some "getters" assume the message being a
string.

* MySensors 2.1.1 release
2017-01-18 21:57:07 +01:00

254 lines
6.3 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;
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.
// Followed by a seek to reset it.
if (read (fd, &c, 1) < 0) {
logError("Interrupt handler error: %s\n", strerror(errno));
break;
}
if (lseek (fd, 0, SEEK_SET) < 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);
}