GitHub Viewer
/*
Copyright (c) The Processing Foundation 2015
Hardware I/O library developed by Gottfried Haider as part of GSoC 2015
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "iface.h"
static const int servo_pulse_oversleep = 35; // amount of uS to account for when sleeping
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_openDevice
(JNIEnv *env, jclass cls, jstring _fn)
{
const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE);
int file = open(fn, O_RDWR);
(*env)->ReleaseStringUTFChars(env, _fn, fn);
if (file < 0) {
return -errno;
} else {
return file;
}
}
JNIEXPORT jstring JNICALL Java_processing_io_NativeInterface_getError
(JNIEnv *env, jclass cls, jint _errno)
{
char *msg = strerror(abs(_errno));
if (msg) {
return (*env)->NewStringUTF(env, msg);
} else {
return NULL;
}
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_closeDevice
(JNIEnv *env, jclass cls, jint handle)
{
if (close(handle) < 0) {
return -errno;
} else {
return 0;
}
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_readFile
(JNIEnv *env, jclass cls, jstring _fn, jbyteArray _in)
{
const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE);
int file = open(fn, O_RDONLY);
(*env)->ReleaseStringUTFChars(env, _fn, fn);
if (file < 0) {
return -errno;
}
jbyte *in = (*env)->GetByteArrayElements(env, _in, NULL);
int len = read(file, in, (*env)->GetArrayLength(env, _in));
if (len < 0) {
len = -errno;
}
(*env)->ReleaseByteArrayElements(env, _in, in, 0);
close(file);
return len;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_writeFile
(JNIEnv *env, jclass cls, jstring _fn, jbyteArray _out)
{
const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE);
int file = open(fn, O_WRONLY);
(*env)->ReleaseStringUTFChars(env, _fn, fn);
if (file < 0) {
return -errno;
}
jbyte *out = (*env)->GetByteArrayElements(env, _out, JNI_FALSE);
int len = write(file, out, (*env)->GetArrayLength(env, _out));
if (len < 0) {
len = -errno;
}
(*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT);
close(file);
return len;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemRead
(JNIEnv *env, jclass cls, jint offset)
{
// validate offset
if (4096 2)
#define BCM2835_GPPUDCLK0_OFFSET (0x98 >> 2)
#define BCM2835_GPPUDCLK1_OFFSET (0x9c >> 2)
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemSetPinBias
(JNIEnv *env, jclass cls, jint gpio, jint mode)
{
int ret = 0; // success
int file = open("/dev/gpiomem", O_RDWR|O_SYNC);
if (file < 0) {
return -errno;
}
uint32_t *mem = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, file, 0);
if (mem == MAP_FAILED) {
close(file);
return -errno;
}
// validate arguments
if (gpio < 0 || 53 < gpio) {
ret = -EINVAL;
goto out;
}
// see BCM2835 datasheet, p. 101
uint32_t pud;
if (mode == 0) {
pud = 0; // floating
} else if (mode == 2) {
pud = 2; // pull-up
} else if (mode == 3) {
pud = 1; // pull-down
} else {
ret = -EINVAL;
goto out;
}
/*
* From the BCM2835 datasheet, p. 101:
*
* The following sequence of events is required:
* 1. Write to GPPUD to set the required control signal (i.e. Pull-up or
* Pull-Down or neither to remove the current Pull-up/down)
* 2. Wait 150 cycles – this provides the required set-up time for the
* control signal
* 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads
* you wish to modify – NOTE only the pads which receive a clock will
* be modified, all others will retain their previous state.
* 4. Wait 150 cycles – this provides the required hold time for the
* control signal
* 5. Write to GPPUD to remove the control signal
* 6. Write to GPPUDCLK0/1 to remove the clock
*/
// python-gpiozero uses a delay of 214 ns, so we do the same
struct timespec wait;
wait.tv_sec = 0;
wait.tv_nsec = 214;
mem[BCM2835_GPPUD_OFFSET] = pud;
nanosleep(&wait, NULL);
if (gpio < 32) {
mem[BCM2835_GPPUDCLK0_OFFSET] = 1 ReleaseStringUTFChars(env, _fn, fn);
if (file < 0) {
return -errno;
}
// dummy read
char tmp;
while (0 < read(file, &tmp, 1));
struct pollfd fds[1];
memset(fds, 0, sizeof(fds));
fds[0].fd = file;
fds[0].events = POLLPRI|POLLERR;
// and wait
int ret = poll(fds, 1, timeout);
close(file);
if (ret < 0) {
return -errno;
} else if (ret == 0) {
// timeout
return 0;
} else if (fds[0].revents & POLLPRI) {
// interrupt
return 1;
} else {
// POLLERR?
return -ENOMSG;
}
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferI2c
(JNIEnv *env, jclass cls, jint handle, jint slave, jbyteArray _out, jbyteArray _in)
{
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg msgs[2];
jbyte *out, *in;
packets.msgs = msgs;
packets.nmsgs = 0;
if (_out != NULL) {
msgs[packets.nmsgs].addr = slave;
msgs[packets.nmsgs].flags = 0;
msgs[packets.nmsgs].len = (*env)->GetArrayLength(env, _out);
out = (*env)->GetByteArrayElements(env, _out, NULL);
msgs[packets.nmsgs].buf = out;
packets.nmsgs++;
}
if (_in != NULL) {
msgs[packets.nmsgs].addr = slave;
msgs[packets.nmsgs].flags = I2C_M_RD; // I2C_M_RECV_LEN is not supported
msgs[packets.nmsgs].len = (*env)->GetArrayLength(env, _in);
in = (*env)->GetByteArrayElements(env, _in, NULL);
msgs[packets.nmsgs].buf = in;
packets.nmsgs++;
}
// set the timeout to 100ms - this helps slow devices such as the
// Arduino Uno to keep up
ioctl(handle, I2C_TIMEOUT, 10);
int ret = ioctl(handle, I2C_RDWR, &packets);
if (ret < 0) {
ret = -errno;
}
if (_out != NULL) {
(*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT);
}
if (_in != NULL) {
(*env)->ReleaseByteArrayElements(env, _in, in, 0);
}
return ret;
}
typedef struct {
int fd;
pthread_t thread;
int pulse;
int period;
} SERVO_STATE_T;
static void* servoThread(void *ptr) {
SERVO_STATE_T *state = (SERVO_STATE_T*)ptr;
struct timespec on, off;
on.tv_sec = 0;
off.tv_sec = 0;
do {
write(state->fd, "1", 1);
on.tv_nsec = state->pulse * 1000;
nanosleep(&on, NULL);
write(state->fd, "0", 1);
off.tv_nsec = (state->period - state->pulse) * 1000;
nanosleep(&off, NULL);
} while (1);
}
JNIEXPORT jlong JNICALL Java_processing_io_NativeInterface_servoStartThread
(JNIEnv *env, jclass cls, jint gpio, jint pulse, jint period)
{
char path[26 + 19 + 1];
int fd;
pthread_t thread;
// setup struct holding our state
SERVO_STATE_T *state = malloc(sizeof(SERVO_STATE_T));
if (!state) {
return -ENOMEM;
}
memset(state, 0, sizeof(*state));
state->pulse = (pulse - servo_pulse_oversleep > 0) ? pulse - servo_pulse_oversleep : 0;
// we're obviously also oversleeping in the general period case
// but other than the pulse, this doesn't seem to be crucial with servos
state->period = period;
// open gpio
sprintf(path, "/sys/class/gpio/gpio%d/value", gpio);
state->fd = open(path, O_WRONLY);
if (state->fd < 0) {
free(state);
return -errno;
}
// start thread
int ret = pthread_create(&state->thread, NULL, servoThread, state);
if (ret != 0) {
free(state);
return -ret;
}
// set scheduling policy and priority
struct sched_param param;
param.sched_priority = 75;
ret = pthread_setschedparam(state->thread, SCHED_FIFO, ¶m);
if (ret != 0) {
fprintf(stderr, "Error setting thread policy: %s\n", strerror(ret));
}
return (intptr_t)state;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoUpdateThread
(JNIEnv *env, jclass cls, jlong handle, jint pulse, jint period)
{
SERVO_STATE_T *state = (SERVO_STATE_T*)(intptr_t)handle;
state->pulse = (pulse - servo_pulse_oversleep > 0) ? pulse - servo_pulse_oversleep : 0;
state->period = period;
return 0;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoStopThread
(JNIEnv *env, jclass cls, jlong handle)
{
SERVO_STATE_T *state = (SERVO_STATE_T*)(intptr_t)handle;
// signal thread to stop
pthread_cancel(state->thread);
pthread_join(state->thread, NULL);
close(state->fd);
free(state);
return 0;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_setSpiSettings
(JNIEnv *env, jclass cls, jint handle, jint _maxSpeed, jint dataOrder, jint mode)
{
uint8_t tmp;
uint32_t maxSpeed;
tmp = (uint8_t)mode;
int ret = ioctl(handle, SPI_IOC_WR_MODE, &tmp);
if (ret < 0) {
return ret;
}
tmp = (uint8_t)dataOrder;
ret = ioctl(handle, SPI_IOC_WR_LSB_FIRST, &tmp);
if (ret < 0) {
return ret;
}
maxSpeed = (uint32_t)_maxSpeed;
ret = ioctl(handle, SPI_IOC_WR_MAX_SPEED_HZ, &maxSpeed);
if (ret < 0) {
return ret;
}
return 0;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferSpi
(JNIEnv *env, jclass cls, jint handle, jbyteArray _out, jbyteArray _in)
{
jbyte* out = (*env)->GetByteArrayElements(env, _out, NULL);
jbyte* in = (*env)->GetByteArrayElements(env, _in, NULL);
struct spi_ioc_transfer xfer = {
.tx_buf = (unsigned long)out,
.rx_buf = (unsigned long)in,
.len = MIN((*env)->GetArrayLength(env, _out), (*env)->GetArrayLength(env, _in)),
};
int ret = ioctl(handle, SPI_IOC_MESSAGE(1), &xfer);
(*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT);
(*env)->ReleaseByteArrayElements(env, _in, in, 0);
return ret;
}