Files
MySensors/drivers/RPi/rpi_util.cpp
T
Marcelo Aquino 69ac4b759a RPi: Add support for I2C
Add MyHw file for RaspberryPi.

Fix compilation warnings in x86.

Fix some error messages.
2016-10-16 13:40:23 -02:00

331 lines
8.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 Marcelo Aquino <marceloaqno@gmail.org>
* Copyright (C) 2016 Marcelo Aquino
* Full contributor list: https://github.com/mysensors/MySensors/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 <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 "rpi_util.h"
#include "SPI.h"
#include "log.h"
extern "C" {
int piHiPri(const int pri);
}
struct ThreadArgs {
void (*func)();
int gpioPin;
};
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,
};
#ifdef __RPI_BPLUS
static uint8_t physToGpio[64] =
{
255, // 0
255, 255, // 1, 2
2, 255,
3, 255,
4, 14,
255, 15,
17, 18,
27, 255,
22, 23,
255, 24,
10, 255,
9, 25,
11, 8,
255, 7, // 25, 26
// B+
0, 1,
5, 255,
6, 12,
13, 255,
19, 16,
26, 20,
255, 21,
// the P5 connector on the Rev 2 boards:
255, 255,
255, 255,
255, 255,
255, 255,
255, 255,
28, 29,
30, 31,
255, 255,
255, 255,
255, 255,
255, 255,
};
#else
static uint8_t physToGpio[64] =
{
255, // 0
255, 255, // 1, 2
0, 255,
1, 255,
4, 14,
255, 15,
17, 18,
21, 255,
22, 23,
255, 24,
10, 255,
9, 25,
11, 8,
255, 7, // 25, 26
255, 255, 255, 255, 255, // ... 31
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // ... 47
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // ... 63
};
#endif
void *interruptHandler(void *args) {
int fd, ret;
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) {
mys_log(LOG_ERR, "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 ...
ret = poll(&polls, 1, -1);
if (ret < 0) {
mys_log(LOG_ERR, "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.
(void)read (fd, &c, 1) ;
lseek (fd, 0, SEEK_SET) ;
// Call user function.
func();
}
close(fd);
return NULL;
}
void rpi_util::pinMode(uint8_t physPin, uint8_t mode) {
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
if (gpioPin == 255) {
mys_log(LOG_ERR, "pinMode: invalid pin: %d\n", physPin);
return;
}
// Check if SPI is in use and target pin is related to SPI
if (SPIClass::is_initialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) {
return;
} else {
bcm2835_gpio_fsel(gpioPin, mode);
}
}
void rpi_util::digitalWrite(uint8_t physPin, uint8_t value) {
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
if (gpioPin == 255) {
mys_log(LOG_ERR, "digitalWrite: invalid pin: %d\n", physPin);
return;
}
// Check if SPI is in use and target pin is related to SPI
if (SPIClass::is_initialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) {
if (value == LOW && (gpioPin == RPI_GPIO_P1_24 || gpioPin == RPI_GPIO_P1_26)) {
SPI.chipSelect(gpioPin);
}
} else {
bcm2835_gpio_write(gpioPin, value);
// Delay to allow any change in state to be reflected in the LEVn, register bit.
delayMicroseconds(1);
}
}
uint8_t rpi_util::digitalRead(uint8_t physPin) {
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
if (gpioPin == 255) {
mys_log(LOG_ERR, "digitalRead: invalid pin: %d\n", physPin);
return 0;
}
// Check if SPI is in use and target pin is related to SPI
if (SPIClass::is_initialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) {
return 0;
} else {
return bcm2835_gpio_lev(gpioPin);
}
}
void rpi_util::attachInterrupt(uint8_t physPin, void (*func)(), uint8_t mode) {
FILE *fd;
char fName[40];
char c;
int count, i;
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
if (gpioPin == 255) {
mys_log(LOG_ERR, "attachInterrupt: invalid pin: %d\n", physPin);
return;
}
if (threadIds[gpioPin] == NULL) {
threadIds[gpioPin] = new pthread_t;
} else {
// Cancel the existing thread for that pin
pthread_cancel(*threadIds[gpioPin]);
// Wait a bit
delay(1L);
}
// Export pin for interrupt
if ((fd = fopen("/sys/class/gpio/export", "w")) == NULL) {
mys_log(LOG_ERR, "attachInterrupt: Unable to export pin %d for interrupt: %s\n", physPin, strerror(errno));
exit(1);
}
fprintf(fd, "%d\n", gpioPin);
fclose(fd);
// Wait a bit the system to create /sys/class/gpio/gpio<GPIO number>
delay(1L);
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/direction", gpioPin) ;
if ((fd = fopen (fName, "w")) == NULL) {
fprintf (stderr, "attachInterrupt: Unable to open GPIO direction interface for pin %d: %s\n", physPin, 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) {
fprintf (stderr, "attachInterrupt: Unable to open GPIO edge interface for pin %d: %s\n", physPin, 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:
mys_log(LOG_ERR, "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) {
fprintf (stderr, "Error reading pin %d: %s\n", physPin, 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) {
mys_log(LOG_ERR, "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 rpi_util::detachInterrupt(uint8_t physPin) {
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
if (gpioPin == 255) {
mys_log(LOG_ERR, "detachInterrupt: invalid pin: %d\n", physPin);
return;
}
// 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) {
mys_log(LOG_ERR, "Unable to unexport pin %d for interrupt\n", gpioPin);
exit(1);
}
fprintf(fp, "%d", gpioPin);
fclose(fp);
}
uint8_t rpi_util::digitalPinToInterrupt(uint8_t physPin) {
// No need to convert the pin to gpio, we do it in attachInterrupt().
return physPin;
}
void rpi_util::interrupts() {
pthread_mutex_unlock(&intMutex);
}
void rpi_util::noInterrupts() {
pthread_mutex_lock(&intMutex);
}