Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 94 additions & 78 deletions src/main/cpp/_nix_based/jssc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,9 @@
#endif
#ifdef __APPLE__
#include <serial/ioss.h>//Needed for IOSSIOSPEED in Mac OS X (Non standard baudrate)
#elif !defined(HAVE_POLL)
// Seems as poll has some portability issues on OsX (Search for "poll" in
// "https://cr.yp.to/docs/unixport.html"). So we only make use of poll on
// all platforms except "__APPLE__".
// If you want to force usage of 'poll', pass "-DHAVE_POLL=1" to gcc.
#define HAVE_POLL 1
#endif

#if HAVE_POLL == 0
#include <sys/select.h>
#else
#include <poll.h>
#endif
#include <sys/select.h>

#include <jni.h>
#include <jssc_SerialNativeInterface.h>
Expand Down Expand Up @@ -527,97 +517,123 @@ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setDTR
*/
JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_writeBytes
(JNIEnv *env, jobject, jlong portHandle, jbyteArray buffer){
jbyte* jBuffer = env->GetByteArrayElements(buffer, JNI_FALSE);
jbyte* jBuffer = NULL;
jint bufferSize = env->GetArrayLength(buffer);
jint result = write(portHandle, jBuffer, (size_t)bufferSize);
env->ReleaseByteArrayElements(buffer, jBuffer, 0);
return result == bufferSize ? JNI_TRUE : JNI_FALSE;
}
fd_set write_fd_set;
int byteRemains = bufferSize;
struct timeval timeout;
int result;
jclass threadClass = env->FindClass("java/lang/Thread");
jmethodID areWeInterruptedMethod = env->GetStaticMethodID(threadClass, "interrupted", "()Z");

/**
* Waits until 'read()' has something to tell for the specified filedescriptor.
*/
static void awaitReadReady(JNIEnv*, jlong fd){
#if HAVE_POLL == 0
// Alternative impl using 'select' as 'poll' isn't available (or broken).

//assert(fd < FD_SETSIZE); // <- Might help when hunting SEGFAULTs.
fd_set readFds;
while(true) {
FD_ZERO(&readFds);
FD_SET(fd, &readFds);
int result = select(fd + 1, &readFds, NULL, NULL, NULL);
if(result < 0){
// man select: On error, -1 is returned, and errno is set to indicate the error
// TODO: Maybe a candidate to raise a java exception. But won't do
// yet for backward compatibility.
continue;
}
// Did wait successfully.
break;
if (portHandle < 0 || portHandle > FD_SETSIZE) {
char msg[95];
snprintf(msg, sizeof msg, "select(): file descriptor %ld outside expected range 0..%d", portHandle, FD_SETSIZE);
return env->ThrowNew(env->FindClass("java/lang/IndexOutOfBoundsException"), msg);
}
FD_CLR(fd, &readFds);

#else
// Default impl using 'poll'. This is more robust against fd>=1024 (eg
// SEGFAULT problems).

struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;
while(true){
int result = poll(fds, 1, -1);
if(result < 0){
// man poll: On error, -1 is returned, and errno is set to indicate the error.
// TODO: Maybe a candidate to raise a java exception. But won't do
// yet for backward compatibility.
jBuffer = env->GetByteArrayElements(buffer, JNI_FALSE);

while(byteRemains > 0) {

// Check if the java thread has been interrupted, and if so, throw the exception
if (env->CallStaticBooleanMethod(threadClass, areWeInterruptedMethod)) {
jclass excClass = env->FindClass("java/lang/InterruptedException");
env->ThrowNew(excClass, "Interrupted while writing to serial port");
// It shouldn't matter what we return, the exception will be thrown right away
break;
}

timeout.tv_sec = 0;
timeout.tv_usec = 100000; // 100 ms
FD_ZERO(&write_fd_set);
FD_SET(portHandle, &write_fd_set);

result = select(portHandle + 1, NULL, &write_fd_set, NULL, &timeout);
if (result < 0) {
env->ThrowNew(env->FindClass("java/io/IOException"), "Waiting for serial port to become writable failed");
// Return value is ignored anyway, exception is handled immidiatly
break;
} else if (result == 0) {
// timeout
continue;
}
// Did wait successfully.
break;

result = write(portHandle, jBuffer + (bufferSize - byteRemains), byteRemains);
if(result < 0){
env->ThrowNew(env->FindClass("java/io/IOException"), "Error writing to serial port");
break;
} else if (result == 0) {
env->ThrowNew(env->FindClass("java/io/IOException"), "Serial port was closed unexpectedly");
break;
} else { // result > 0
byteRemains -= result;
}
}

#endif
env->ReleaseByteArrayElements(buffer, jBuffer, 0);
return JNI_TRUE; //result == bufferSize ? JNI_TRUE : JNI_FALSE;
}

/* OK */

/*
* Reading data from the port
*
* Rewritten to use poll() instead of select() to handle fd>=1024
*/
JNIEXPORT jbyteArray JNICALL Java_jssc_SerialNativeInterface_readBytes
(JNIEnv *env, jobject, jlong portHandle, jint byteCount){
fd_set read_fd_set;
jbyte *lpBuffer = NULL;
int byteRemains = byteCount;
struct timeval timeout;
int result;
jclass threadClass = env->FindClass("java/lang/Thread");
jmethodID areWeInterruptedMethod = env->GetStaticMethodID(threadClass, "interrupted", "()Z");

// TODO: Errors should be communicated by raising java exceptions; Will break
// backwards compatibility.
if (portHandle < 0 || portHandle > FD_SETSIZE) {
char msg[95];
snprintf(msg, sizeof msg, "select(): file descriptor %ld outside expected range 0..%d", portHandle, FD_SETSIZE);
env->ThrowNew(env->FindClass("java/lang/IndexOutOfBoundsException"), msg);
return NULL;
}

jbyte *lpBuffer = new jbyte[byteCount];
jbyteArray returnArray = NULL;
int byteRemains = byteCount;
lpBuffer = new jbyte[byteCount];

while(byteRemains > 0) {
int result = 0;

awaitReadReady(env, portHandle);

errno = 0;
result = read(portHandle, lpBuffer + (byteCount - byteRemains), byteRemains);
if (result < 0) {
// man read: On error, -1 is returned, and errno is set to indicate the error.
// TODO: May candidate for raising a java exception. See comment at begin of function.
// Check if the java thread has been interrupted, and if so, throw the exception
if (env->CallStaticBooleanMethod(threadClass, areWeInterruptedMethod)) {
jclass excClass = env->FindClass("java/lang/InterruptedException");
env->ThrowNew(excClass, "Interrupted while reading from serial port");
// It shouldn't matter what we return, the exception will be thrown right away
break;
}
else if (result == 0) {
// AFAIK this happens either on EOF or on EWOULDBLOCK (see 'man read').
// TODO: Is "just continue" really the right thing to do? I will keep it that
// way because the old code did so and I don't know better.

timeout.tv_sec = 0;
timeout.tv_usec = 100000; // 100 ms
FD_ZERO(&read_fd_set);
FD_SET(portHandle, &read_fd_set);

result = select(portHandle + 1, &read_fd_set, NULL, NULL, &timeout);
if (result < 0) {
env->ThrowNew(env->FindClass("java/io/IOException"), "Error while waiting for input from serial port");
// Return value is ignored anyway, exception is handled immidiatly
break;
} else if (result == 0) {
// timeout
continue;
}
else {

result = read(portHandle, lpBuffer + (byteCount - byteRemains), byteRemains);
if(result < 0){
env->ThrowNew(env->FindClass("java/io/IOException"), "Error reading from serial port");
break;
} else if (result == 0) {
env->ThrowNew(env->FindClass("java/io/IOException"), "Serial port was closed unexpectedly");
break;
} else { // result > 0
byteRemains -= result;
}
}

returnArray = env->NewByteArray(byteCount);
jbyteArray returnArray = env->NewByteArray(byteCount);
env->SetByteArrayRegion(returnArray, 0, byteCount, lpBuffer);
delete[] lpBuffer;
return returnArray;
Expand Down
3 changes: 2 additions & 1 deletion src/main/cpp/jssc_SerialNativeInterface.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading