#include "rtapi.h"
#include "rtapi_bitops.h"
#include "rtapi_app.h"
#include "hal.h"
#include "bcm2835.h"
#include "cpuinfo.h"
#define BCM2708_PERI_BASE 0x20000000
#define BCM2708_GPIO_BASE (BCM2708_PERI_BASE + 0x200000)
#define BCM2709_PERI_BASE 0x3F000000
#define BCM2709_GPIO_BASE (BCM2709_PERI_BASE + 0x200000)
#define RTAPI_BIT(nr) (1UL << (nr))
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
static unsigned char rev1_gpios[] = {0, 1, 4, 7, 8, 9, 10, 11, 14, 15, 17, 18, 21, 22, 23, 24, 25};
static unsigned char rev1_pins[] = {3, 5, 7, 26, 24, 21, 19, 23, 8, 10, 11, 12, 13, 15, 16, 18, 22};
static unsigned char rev2_gpios[] = {2, 3, 4, 7, 8, 9, 10, 11, 14, 15, 17, 18, 22, 23, 24, 25, 27};
static unsigned char rev2_pins[] = {3, 5, 7, 26, 24, 21, 19, 23, 8, 10, 11, 12, 15, 16, 18, 22, 13};
static unsigned char rpi2_gpios[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 21, 23, 24, 25, 26, 27 };
static unsigned char rpi2_pins[] = {3, 5, 7, 29, 31, 26, 24, 21, 19, 23, 32, 33, 8, 10, 36, 11, 12, 35, 38, 15, 40, 16, 18, 22, 37, 13 };
static int npins;
static int mem_fd;
static volatile unsigned *gpio;
MODULE_AUTHOR("Michael Haberler");
MODULE_DESCRIPTION("Driver for Raspberry Pi GPIO pins");
MODULE_LICENSE("GPL");
static char *dir = "-1"; RTAPI_MP_STRING(dir, "port direction, 1=output");
static unsigned dir_map;
static char *exclude = "0"; RTAPI_MP_STRING(exclude, "exclude pins, 1=dont use");
static unsigned exclude_map;
static int comp_id;
static unsigned char *pins, *gpios;
hal_bit_t **port_data;
static void write_port(void *arg, long period);
static void read_port(void *arg, long period);
static __inline__ uint32_t bcm2835_peri_read(volatile uint32_t* paddr)
{
volatile uint32_t ret = *paddr;
return ret;
}
static __inline__ uint8_t bcm2835_gpio_lev(uint8_t pin)
{
volatile uint32_t* paddr = gpio + BCM2835_GPLEV0/4 + pin/32;
uint8_t shift = pin % 32;
uint32_t value = bcm2835_peri_read(paddr);
return (value & (1 << shift)) ? HIGH : LOW;
}
static __inline__ void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value)
{
*paddr = value;
*paddr = value;
}
static __inline__ void bcm2835_gpio_set(uint8_t pin)
{
volatile uint32_t* paddr = gpio + BCM2835_GPSET0/4 + pin/32;
uint8_t shift = pin % 32;
bcm2835_peri_write(paddr, 1 << shift);
}
static __inline__ void bcm2835_gpio_clr(uint8_t pin)
{
volatile uint32_t* paddr = gpio + BCM2835_GPCLR0/4 + pin/32;
uint8_t shift = pin % 32;
bcm2835_peri_write(paddr, 1 << shift);
}
static __inline__ void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask)
{
uint32_t v = bcm2835_peri_read(paddr);
v = (v & ~mask) | (value & mask);
bcm2835_peri_write(paddr, v);
}
void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode)
{
volatile uint32_t* paddr = gpio + BCM2835_GPFSEL0/4 + (pin/10);
uint8_t shift = (pin % 10) * 3;
uint32_t mask = BCM2835_GPIO_FSEL_MASK << shift;
uint32_t value = mode << shift;
bcm2835_peri_set_bits(paddr, value, mask);
}
static int setup_gpiomem_access(void)
{
if ((mem_fd = open("/dev/gpiomem", O_RDWR|O_SYNC)) < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: can't open /dev/gpiomem: %d - %s\n"
"If the error is 'permission denied' then try adding the user who runs\n"
"LinuxCNC to the gpio group: sudo gpasswd -a username gpio\n", errno, strerror(errno));
return -1;
}
gpio = mmap(NULL, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, 0);
if (gpio == MAP_FAILED) {
close(mem_fd);
mem_fd = -1;
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: mmap failed: %d - %s\n", errno, strerror(errno));
return -1;
}
return 0;
}
static int setup_gpio_access(int rev, int ncores)
{
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: can't open /dev/mem: %d - %s\n",
errno, strerror(errno));
return -1;
}
if (rev <= 2 || ncores <= 2)
gpio = mmap(NULL, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE,
MAP_SHARED, mem_fd, BCM2708_GPIO_BASE);
else
gpio = mmap(NULL, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE,
MAP_SHARED, mem_fd, BCM2709_GPIO_BASE);
if (gpio == MAP_FAILED) {
rtapi_print_msg(RTAPI_MSG_ERR,
"HAL_PI_GPIO: mmap failed: %d - %s\n",
errno, strerror(errno));
return -1;;
}
return 0;
}
static int number_of_cores(void)
{
char str[256];
int procCount = 0;
FILE *fp;
if( (fp = fopen("/proc/cpuinfo", "r")) ) {
while(fgets(str, sizeof str, fp))
if( !memcmp(str, "processor", 9) ) procCount++;
}
if ( !procCount ) {
rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: Unable to get proc count. Defaulting to 2");
procCount = 2;
}
return procCount;
}
int rtapi_app_main(void)
{
int n, retval = 0;
int rev, ncores, pinno;
char *endptr;
if ((rev = get_rpi_revision()) < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"unrecognized Raspberry revision, see /proc/cpuinfo\n");
return -EINVAL;
}
ncores = number_of_cores();
rtapi_print_msg(RTAPI_MSG_INFO, "%d cores rev %d", ncores, rev);
switch (rev) {
case 5:
rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry4\n");
pins = rpi2_pins;
gpios = rpi2_gpios;
npins = sizeof(rpi2_pins);
break;
case 4:
rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry3\n");
pins = rpi2_pins;
gpios = rpi2_gpios;
npins = sizeof(rpi2_pins);
break;
case 3:
rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry2\n");
pins = rpi2_pins;
gpios = rpi2_gpios;
npins = sizeof(rpi2_pins);
break;
case 1:
rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry1 rev 1.0\n");
pins = rev1_pins;
gpios = rev1_gpios;
npins = sizeof(rev1_pins);
break;
case 2:
rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry1 Rev 2.0\n");
pins = rev2_pins;
gpios = rev2_gpios;
npins = sizeof(rev2_pins);
break;
default:
rtapi_print_msg(RTAPI_MSG_ERR,
"HAL_PI_GPIO: ERROR: board revision %d not supported\n", rev);
return -EINVAL;
}
port_data = hal_malloc(npins * sizeof(void *));
if (port_data == 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"HAL_PI_GPIO: ERROR: hal_malloc() failed\n");
hal_exit(comp_id);
return -1;
}
if (dir == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: no config string\n");
return -1;
}
dir_map = strtoul(dir, &endptr,0);
if (*endptr) {
rtapi_print_msg(RTAPI_MSG_ERR,
"HAL_PI_GPIO: dir=%s - trailing garbage: '%s'\n",
dir, endptr);
return -1;
}
if (exclude == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: no exclude string\n");
return -1;
}
exclude_map = strtoul(exclude, &endptr,0);
if (*endptr) {
rtapi_print_msg(RTAPI_MSG_ERR,
"HAL_PI_GPIO: exclude=%s - trailing garbage: '%s'\n",
exclude, endptr);
return -1;
}
if (setup_gpiomem_access()) {
if (setup_gpio_access(rev, ncores))
return -1;
}
comp_id = hal_init("hal_pi_gpio");
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"HAL_PI_GPIO: ERROR: hal_init() failed\n");
return -1;
}
for (n = 0; n < npins; n++) {
if (exclude_map & RTAPI_BIT(n))
continue;
pinno = pins[n];
if (dir_map & RTAPI_BIT(n)) {
bcm2835_gpio_fsel(gpios[n], BCM2835_GPIO_FSEL_OUTP);
if ((retval = hal_pin_bit_newf(HAL_IN, &port_data[n],
comp_id, "hal_pi_gpio.pin-%02d-out", pinno)) < 0)
break;
} else {
bcm2835_gpio_fsel(gpios[n], BCM2835_GPIO_FSEL_INPT);
if ((retval = hal_pin_bit_newf(HAL_OUT, &port_data[n],
comp_id, "hal_pi_gpio.pin-%02d-in", pinno)) < 0)
break;
}
}
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"HAL_PI_GPIO: ERROR: pin %d export failed with err=%i\n",
n,retval);
hal_exit(comp_id);
return -1;
}
retval = hal_export_funct("hal_pi_gpio.write", write_port, 0,
0, 0, comp_id);
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"HAL_PI_GPIO: ERROR: write funct export failed\n");
hal_exit(comp_id);
return -1;
}
retval = hal_export_funct("hal_pi_gpio.read", read_port, 0,
0, 0, comp_id);
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"HAL_PI_GPIO: ERROR: read funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO,
"HAL_PI_GPIO: installed driver\n");
hal_ready(comp_id);
return 0;
}
void rtapi_app_exit(void)
{
if (gpio)
munmap((void *) gpio, BCM2835_BLOCK_SIZE);
if (mem_fd > -1)
close(mem_fd);
hal_exit(comp_id);
}
static void write_port(void *arg, long period)
{
int n;
for (n = 0; n < npins; n++) {
if (exclude_map & RTAPI_BIT(n))
continue;
if (dir_map & RTAPI_BIT(n)) {
if (*(port_data[n])) {
bcm2835_gpio_set(gpios[n]);
} else {
bcm2835_gpio_clr(gpios[n]);
}
}
}
}
static void read_port(void *arg, long period)
{
int n;
for (n = 0; n < npins; n++) {
if ((~dir_map & RTAPI_BIT(n)) && (~exclude_map & RTAPI_BIT(n)))
*port_data[n] = bcm2835_gpio_lev(gpios[n]);
}
}