diff options
Diffstat (limited to 'flow-native/src')
-rw-r--r-- | flow-native/src/.gitignore | 8 | ||||
-rw-r--r-- | flow-native/src/CMakeLists.txt | 56 | ||||
-rw-r--r-- | flow-native/src/flow_jni.c | 243 | ||||
-rw-r--r-- | flow-native/src/include/ch_jodersky_flow_UnsafeSerial.h | 45 | ||||
-rw-r--r-- | flow-native/src/include/ch_jodersky_flow_UnsafeSerial__.h | 29 | ||||
-rw-r--r-- | flow-native/src/include/com_github_jodersky_flow_internal_NativeSerial.h | 83 | ||||
-rw-r--r-- | flow-native/src/include/flow.h | 79 | ||||
-rw-r--r-- | flow-native/src/platform/posix/flow.c | 394 |
8 files changed, 469 insertions, 468 deletions
diff --git a/flow-native/src/.gitignore b/flow-native/src/.gitignore index e259f31..1785c46 100644 --- a/flow-native/src/.gitignore +++ b/flow-native/src/.gitignore @@ -1,8 +1,8 @@ # CMake -CMakeFiles/ -CMakeCache.txt -cmake_install.cmake -Makefile +/CMakeFiles/ +/CMakeCache.txt +/cmake_install.cmake +/Makefile # Binary files *.o diff --git a/flow-native/src/CMakeLists.txt b/flow-native/src/CMakeLists.txt index 7f6cbd7..8be2fab 100644 --- a/flow-native/src/CMakeLists.txt +++ b/flow-native/src/CMakeLists.txt @@ -1,23 +1,24 @@ -cmake_minimum_required(VERSION 2.6) +################################################################ +# A minimal CMake file that is compatible with sbt-jni # +# # +# All settings required by sbt-jni have been marked so, please # +# add/modify/remove settings to build your specific library. # +################################################################ + +cmake_minimum_required(VERSION 2.8.0) # Define project and related variables +# (required by sbt-jni) please use semantic versioning # project (flow) +set(PROJECT_VERSION_MAJOR 4) +set(PROJECT_VERSION_MINOR 0) +set(PROJECT_VERSION_PATCH 0) -# Set versions and library name -# Note: major version will be appended to library name -# -set (VERSION_MAJOR 3) -set (VERSION_MINOR 0) -set (VERSION_PATCH 1) -set (LIB_NAME flow${VERSION_MAJOR}) - -# Command-line options -# -# required by sbt-jni to install binaries to correct places -set (LIB_INSTALL_DIR lib CACHE PATH "Path in which to install libraries (Autoconf equivalent to --libdir).") -# required by sbt-jni to disable versioned libraries -set (ENABLE_VERSIONED_LIB ON CACHE BOOLEAN "Generate versioned library files and symlinks.") +set(CMAKE_C_FLAGS "-std=c99") +add_definitions(-Wall) +add_definitions(-Wextra) +add_definitions(-pedantic) # Setup JNI find_package(JNI REQUIRED) @@ -26,20 +27,19 @@ if (JNI_FOUND) endif() # Include directories +include_directories(.) include_directories(include) include_directories(${JNI_INCLUDE_DIRS}) -# Setup main shared library -# Note: major version is appended to library name -add_library(${LIB_NAME} SHARED flow_jni.c platform/posix/flow.c) -if (ENABLE_VERSIONED_LIB) - set_target_properties( - ${LIB_NAME} - PROPERTIES - VERSION 0.${VERSION_MINOR}.${VERSION_PATCH} # major version always 0, it is included in name - SOVERSION 0 - ) -endif() +# Sources +file(GLOB LIB_SRC + "*.c" + "platform/posix/*.c" +) -# Installation targets -install(TARGETS ${LIB_NAME} LIBRARY DESTINATION ${LIB_INSTALL_DIR}) +# Setup installation targets +# (required by sbt-jni) major version should always be appended to library name +# +set (LIB_NAME ${PROJECT_NAME}${PROJECT_VERSION_MAJOR}) +add_library(${LIB_NAME} SHARED ${LIB_SRC}) +install(TARGETS ${LIB_NAME} LIBRARY DESTINATION .) diff --git a/flow-native/src/flow_jni.c b/flow-native/src/flow_jni.c index 8ec2aed..75bffff 100644 --- a/flow-native/src/flow_jni.c +++ b/flow-native/src/flow_jni.c @@ -1,169 +1,150 @@ +#include <stdint.h> + #include "flow.h" -#include "com_github_jodersky_flow_internal_NativeSerial.h" +#include "ch_jodersky_flow_UnsafeSerial.h" +#include "ch_jodersky_flow_UnsafeSerial__.h" -static inline void throwException(JNIEnv* env, const char* const exception, const char * const message) { - (*env)->ThrowNew(env, (*env)->FindClass(env, exception), message); -} +// suppress unused parameter warnings +#define UNUSED_ARG(x) (void)(x) -static inline void check(JNIEnv* env, int id) { - switch (id) { - case E_IO: throwException(env, "java/io/IOException", ""); break; - case E_BUSY: throwException(env, "com/github/jodersky/flow/PortInUseException", ""); break; - case E_ACCESS_DENIED: throwException(env, "com/github/jodersky/flow/AccessDeniedException", ""); break; - case E_INVALID_SETTINGS: throwException(env, "com/github/jodersky/flow/InvalidSettingsException", ""); break; - case E_INTERRUPT: throwException(env, "com/github/jodersky/flow/PortInterruptedException", ""); break; - case E_NO_PORT: throwException(env, "com/github/jodersky/flow/NoSuchPortException", ""); break; - default: return; - } +static inline void throwException(JNIEnv* env, const char* const exception, const char * const message) +{ + (*env)->ThrowNew(env, (*env)->FindClass(env, exception), message); } - -/* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: open - * Signature: (Ljava/lang/String;IIZI)J - */ -JNIEXPORT jlong JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_open - (JNIEnv *env, jclass clazz, jstring port_name, jint baud, jint char_size, jboolean two_stop_bits, jint parity) { - - const char *dev = (*env)->GetStringUTFChars(env, port_name, 0); - struct serial_config* config; - int r = serial_open(dev, baud, char_size, two_stop_bits, parity, &config); - (*env)->ReleaseStringUTFChars(env, port_name, dev); - - - if (r < 0) { - check(env, r); - return 0; - } - - long jpointer = (long) config; - return jpointer; +/** Check return code and throw exception in case it is non-zero. */ +static void check(JNIEnv* env, int ret) +{ + switch (ret) { + case -E_IO: throwException(env, "java/io/IOException", ""); break; + case -E_BUSY: throwException(env, "ch/jodersky/flow/PortInUseException", ""); break; + case -E_ACCESS_DENIED: throwException(env, "ch/jodersky/flow/AccessDeniedException", ""); break; + case -E_INVALID_SETTINGS: throwException(env, "ch/jodersky/flow/InvalidSettingsException", ""); break; + case -E_INTERRUPT: throwException(env, "ch/jodersky/flow/PortInterruptedException", ""); break; + case -E_NO_PORT: throwException(env, "ch/jodersky/flow/NoSuchPortException", ""); break; + default: return; + } +} +/** Get pointer to serial config associated to an UnsafeSerial instance. */ +static struct serial_config* get_config(JNIEnv* env, jobject unsafe_serial) +{ + jclass clazz = (*env)->FindClass(env, "ch/jodersky/flow/UnsafeSerial"); + jfieldID field = (*env)->GetFieldID(env, clazz, "serialAddr", "J"); + jlong addr = (*env)->GetLongField(env, unsafe_serial, field); + return (struct serial_config*) (intptr_t) addr; } /* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: readDirect - * Signature: (JLjava/nio/ByteBuffer;)I + * Class: ch_jodersky_flow_UnsafeSerial__ + * Method: open + * Signature: (Ljava/lang/String;IIZI)J */ -JNIEXPORT jint JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_readDirect - (JNIEnv *env, jclass clazz, jlong config, jobject buffer) { - - char* local_buffer = (char*) (*env)->GetDirectBufferAddress(env, buffer); - if (local_buffer == NULL) { - throwException(env, "java/lang/IllegalArgumentException", "ByteBuffer not direct"); - return -1; - } - jlong size = (*env)->GetDirectBufferCapacity(env, buffer); - - int r = serial_read((struct serial_config*) config, local_buffer, (size_t) size); - if (r < 0) { - check(env, r); - return -1; - } - return r; - +JNIEXPORT jlong JNICALL Java_ch_jodersky_flow_UnsafeSerial_00024_open +(JNIEnv *env, jobject instance, jstring port_name, jint baud, jint char_size, jboolean two_stop_bits, jint parity) +{ + UNUSED_ARG(instance); + + const char *dev = (*env)->GetStringUTFChars(env, port_name, 0); + struct serial_config* config; + int r = serial_open(dev, baud, char_size, two_stop_bits, parity, &config); + (*env)->ReleaseStringUTFChars(env, port_name, dev); + + if (r < 0) { + check(env, r); + return -E_IO; + } + + long jpointer = (long) config; + return (jlong) jpointer; } /* - * Class: com_github_jodersky_flow_internal_NativeSerial + * Class: ch_jodersky_flow_UnsafeSerial * Method: read - * Signature: (J[B)I + * Signature: (Ljava/nio/ByteBuffer;)I */ -JNIEXPORT jint JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_read - (JNIEnv *env, jclass clazz, jlong config, jbyteArray buffer) { - - jsize size = (*env)->GetArrayLength(env, buffer); - char local_buffer[size]; - int r = serial_read((struct serial_config*) config, local_buffer, size); - if (r < 0) { - check(env, r); - return -1; - } - - (*env)->SetByteArrayRegion(env, buffer, 0, r, (signed char *) local_buffer); - return r; +JNIEXPORT jint JNICALL Java_ch_jodersky_flow_UnsafeSerial_read +(JNIEnv *env, jobject instance, jobject buffer) +{ + char* local_buffer = (char*) (*env)->GetDirectBufferAddress(env, buffer); + if (local_buffer == NULL) { + throwException(env, "java/lang/IllegalArgumentException", "buffer is not direct"); + return -E_IO; + } + size_t size = (size_t) (*env)->GetDirectBufferCapacity(env, buffer); + struct serial_config* config = get_config(env, instance); + + int r = serial_read(config, local_buffer, size); + if (r < 0) { + check(env, r); + } + return r; } /* - * Class: com_github_jodersky_flow_internal_NativeSerial + * Class: ch_jodersky_flow_UnsafeSerial * Method: cancelRead - * Signature: (J)V + * Signature: ()V */ -JNIEXPORT void JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_cancelRead - (JNIEnv *env, jclass clazz, jlong config) { - - int r = serial_cancel_read((struct serial_config*) config); - if (r < 0) { - check(env, r); - } - +JNIEXPORT void JNICALL Java_ch_jodersky_flow_UnsafeSerial_cancelRead +(JNIEnv *env, jobject instance) +{ + int r = serial_cancel_read(get_config(env, instance)); + if (r < 0) { + check(env, r); + } } /* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: writeDirect - * Signature: (JLjava/nio/ByteBuffer;I)I - */ -JNIEXPORT jint JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_writeDirect - (JNIEnv *env, jclass clazz, jlong config, jobject buffer, jint size) { - - char* local_buffer = (char *) (*env)->GetDirectBufferAddress(env, buffer); - if (local_buffer == NULL) { - throwException(env, "java/lang/IllegalArgumentException", "ByteBuffer not direct"); - return -1; - } - - int r = serial_write((struct serial_config*) config, local_buffer, (size_t) size); - if (r < 0) { - check(env, r); - return -1; - } - return r; - -} - -/* - * Class: com_github_jodersky_flow_internal_NativeSerial + * Class: ch_jodersky_flow_UnsafeSerial * Method: write - * Signature: (J[BI)I + * Signature: (Ljava/nio/ByteBuffer;I)I */ -JNIEXPORT jint JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_write - (JNIEnv *env, jclass clazz, jlong config, jbyteArray buffer, jint size) { - - char* local_buffer = (char*) (*env)->GetByteArrayElements(env, buffer, NULL); - int r = serial_write((struct serial_config*) config, local_buffer, size); - (*env)->ReleaseByteArrayElements(env, buffer, (signed char*) local_buffer, JNI_ABORT); - if (r < 0) { - check(env, r); - return -1; - } - return r; - +JNIEXPORT jint JNICALL Java_ch_jodersky_flow_UnsafeSerial_write +(JNIEnv *env, jobject instance, jobject buffer, jint size) +{ + + char* local_buffer = (char *) (*env)->GetDirectBufferAddress(env, buffer); + if (local_buffer == NULL) { + throwException(env, "java/lang/IllegalArgumentException", "buffer is not direct"); + return -E_IO; + } + + int r = serial_write(get_config(env, instance), local_buffer, (size_t) size); + if (r < 0) { + check(env, r); + return -E_IO; + } + return r; } /* - * Class: com_github_jodersky_flow_internal_NativeSerial + * Class: ch_jodersky_flow_UnsafeSerial * Method: close - * Signature: (J)V + * Signature: ()V */ -JNIEXPORT void JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_close - (JNIEnv *env, jclass clazz, jlong config) { - int r = serial_close((struct serial_config*) config); - if (r < 0) { - check(env, r); - } +JNIEXPORT void JNICALL Java_ch_jodersky_flow_UnsafeSerial_close +(JNIEnv *env, jobject instance) +{ + int r = serial_close(get_config(env, instance)); + if (r < 0) { + check(env, r); + } } /* - * Class: com_github_jodersky_flow_internal_NativeSerial + * Class: ch_jodersky_flow_UnsafeSerial__ * Method: debug * Signature: (Z)V */ -JNIEXPORT void JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_debug - (JNIEnv *env, jclass clazz, jboolean value) { - serial_debug((bool) value); -}
\ No newline at end of file +JNIEXPORT void JNICALL Java_ch_jodersky_flow_UnsafeSerial_00024_debug +(JNIEnv *env, jobject instance, jboolean value) +{ + UNUSED_ARG(env); + UNUSED_ARG(instance); + + serial_debug((bool) value); +} diff --git a/flow-native/src/include/ch_jodersky_flow_UnsafeSerial.h b/flow-native/src/include/ch_jodersky_flow_UnsafeSerial.h new file mode 100644 index 0000000..f80ada0 --- /dev/null +++ b/flow-native/src/include/ch_jodersky_flow_UnsafeSerial.h @@ -0,0 +1,45 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class ch_jodersky_flow_UnsafeSerial */ + +#ifndef _Included_ch_jodersky_flow_UnsafeSerial +#define _Included_ch_jodersky_flow_UnsafeSerial +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: ch_jodersky_flow_UnsafeSerial + * Method: read + * Signature: (Ljava/nio/ByteBuffer;)I + */ +JNIEXPORT jint JNICALL Java_ch_jodersky_flow_UnsafeSerial_read + (JNIEnv *, jobject, jobject); + +/* + * Class: ch_jodersky_flow_UnsafeSerial + * Method: cancelRead + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_ch_jodersky_flow_UnsafeSerial_cancelRead + (JNIEnv *, jobject); + +/* + * Class: ch_jodersky_flow_UnsafeSerial + * Method: write + * Signature: (Ljava/nio/ByteBuffer;I)I + */ +JNIEXPORT jint JNICALL Java_ch_jodersky_flow_UnsafeSerial_write + (JNIEnv *, jobject, jobject, jint); + +/* + * Class: ch_jodersky_flow_UnsafeSerial + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_ch_jodersky_flow_UnsafeSerial_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/flow-native/src/include/ch_jodersky_flow_UnsafeSerial__.h b/flow-native/src/include/ch_jodersky_flow_UnsafeSerial__.h new file mode 100644 index 0000000..617875f --- /dev/null +++ b/flow-native/src/include/ch_jodersky_flow_UnsafeSerial__.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class ch_jodersky_flow_UnsafeSerial__ */ + +#ifndef _Included_ch_jodersky_flow_UnsafeSerial__ +#define _Included_ch_jodersky_flow_UnsafeSerial__ +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: ch_jodersky_flow_UnsafeSerial__ + * Method: open + * Signature: (Ljava/lang/String;IIZI)J + */ +JNIEXPORT jlong JNICALL Java_ch_jodersky_flow_UnsafeSerial_00024_open + (JNIEnv *, jobject, jstring, jint, jint, jboolean, jint); + +/* + * Class: ch_jodersky_flow_UnsafeSerial__ + * Method: debug + * Signature: (Z)V + */ +JNIEXPORT void JNICALL Java_ch_jodersky_flow_UnsafeSerial_00024_debug + (JNIEnv *, jobject, jboolean); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/flow-native/src/include/com_github_jodersky_flow_internal_NativeSerial.h b/flow-native/src/include/com_github_jodersky_flow_internal_NativeSerial.h deleted file mode 100644 index 04364fb..0000000 --- a/flow-native/src/include/com_github_jodersky_flow_internal_NativeSerial.h +++ /dev/null @@ -1,83 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include <jni.h> -/* Header for class com_github_jodersky_flow_internal_NativeSerial */ - -#ifndef _Included_com_github_jodersky_flow_internal_NativeSerial -#define _Included_com_github_jodersky_flow_internal_NativeSerial -#ifdef __cplusplus -extern "C" { -#endif -#undef com_github_jodersky_flow_internal_NativeSerial_PARITY_NONE -#define com_github_jodersky_flow_internal_NativeSerial_PARITY_NONE 0L -#undef com_github_jodersky_flow_internal_NativeSerial_PARITY_ODD -#define com_github_jodersky_flow_internal_NativeSerial_PARITY_ODD 1L -#undef com_github_jodersky_flow_internal_NativeSerial_PARITY_EVEN -#define com_github_jodersky_flow_internal_NativeSerial_PARITY_EVEN 2L -/* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: open - * Signature: (Ljava/lang/String;IIZI)J - */ -JNIEXPORT jlong JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_open - (JNIEnv *, jclass, jstring, jint, jint, jboolean, jint); - -/* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: readDirect - * Signature: (JLjava/nio/ByteBuffer;)I - */ -JNIEXPORT jint JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_readDirect - (JNIEnv *, jclass, jlong, jobject); - -/* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: read - * Signature: (J[B)I - */ -JNIEXPORT jint JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_read - (JNIEnv *, jclass, jlong, jbyteArray); - -/* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: cancelRead - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_cancelRead - (JNIEnv *, jclass, jlong); - -/* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: writeDirect - * Signature: (JLjava/nio/ByteBuffer;I)I - */ -JNIEXPORT jint JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_writeDirect - (JNIEnv *, jclass, jlong, jobject, jint); - -/* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: write - * Signature: (J[BI)I - */ -JNIEXPORT jint JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_write - (JNIEnv *, jclass, jlong, jbyteArray, jint); - -/* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: close - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_close - (JNIEnv *, jclass, jlong); - -/* - * Class: com_github_jodersky_flow_internal_NativeSerial - * Method: debug - * Signature: (Z)V - */ -JNIEXPORT void JNICALL Java_com_github_jodersky_flow_internal_NativeSerial_debug - (JNIEnv *, jclass, jboolean); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/flow-native/src/include/flow.h b/flow-native/src/include/flow.h index 44e2a47..e3f33b9 100644 --- a/flow-native/src/include/flow.h +++ b/flow-native/src/include/flow.h @@ -8,22 +8,25 @@ extern "C" { #include <stdbool.h> #include <stddef.h> -//general error codes that are returned by functions -#define E_IO -1 //IO error -#define E_ACCESS_DENIED -2 //access denied -#define E_BUSY -3 // port is busy -#define E_INVALID_SETTINGS -4 // some port settings are invalid -#define E_INTERRUPT -5 // not really an error, function call aborted because port is closed -#define E_NO_PORT -6 //requested port does not exist +// general error codes, whose that are returned by functions +#define E_IO 1 // IO error +#define E_ACCESS_DENIED 2 // access denied +#define E_BUSY 3 // port is busy +#define E_INVALID_SETTINGS 4 // some port settings are invalid +#define E_INTERRUPT 5 // not really an error, function call aborted because port is closed +#define E_NO_PORT 6 // requested port does not exist #define PARITY_NONE 0 #define PARITY_ODD 1 #define PARITY_EVEN 2 -/** Contains internal configuration of an open serial port. */ +/** + * Contains internal configuration of an open serial port. + */ struct serial_config; -/**Opens a serial port and allocates memory for storing configuration. Note: if this function fails, +/** + * Opens a serial port and allocates memory for storing configuration. Note: if this function fails, * any internally allocated resources will be freed. * @param port_name name of port * @param baud baud rate @@ -32,59 +35,69 @@ struct serial_config; * @param parity kind of parity checking to use * @param serial pointer to memory that will be allocated with a serial structure * @return 0 on success - * @return E_NO_PORT if the given port does not exist - * @return E_ACCESS_DENIED if permissions are not sufficient to open port - * @return E_BUSY if port is already in use - * @return E_INVALID_SETTINGS if any of the specified settings are invalid - * @return E_IO on other error */ + * @return -E_NO_PORT if the given port does not exist + * @return -E_ACCESS_DENIED if permissions are not sufficient to open port + * @return -E_BUSY if port is already in use + * @return -E_INVALID_SETTINGS if any of the specified settings are invalid + * @return -E_IO on other error + */ int serial_open( - const char* port_name, - int baud, - int char_size, - bool two_stop_bits, - int parity, - struct serial_config** const serial); + const char* port_name, + int baud, + int char_size, + bool two_stop_bits, + int parity, + struct serial_config** const serial); -/**Closes a previously opened serial port and frees memory containing the configuration. Note: after a call to +/** + * Closes a previously opened serial port and frees memory containing the configuration. Note: after a call to * this function, the 'serial' pointer will become invalid, make sure you only call it once. This function is NOT - * thread safe, make sure no read or write is in prgress when this function is called (the reason is that per - * close manual page, close should not be called on a file descriptor that is in use by another thread). + * thread safe, make sure no read or write is in prgress when this function is called (the reason is that per + * close manual page, close should not be called on a file descriptor that is in use by another thread). * @param serial pointer to serial configuration that is to be closed (and freed) * @return 0 on success - * @return E_IO on error */ + * @return -E_IO on error + */ int serial_close(struct serial_config* const serial); -/**Starts a read from a previously opened serial port. The read is blocking, however it may be +/** + * Starts a read from a previously opened serial port. The read is blocking, however it may be * interrupted by calling 'serial_cancel_read' on the given serial port. * @param serial pointer to serial configuration from which to read * @param buffer buffer into which data is read * @param size maximum buffer size * @return n>0 the number of bytes read into buffer - * @return E_INTERRUPT if the call to this function was interrupted - * @return E_IO on IO error */ + * @return -E_INTERRUPT if the call to this function was interrupted + * @return -E_IO on IO error + */ int serial_read(struct serial_config* const serial, char* const buffer, size_t size); -/**Cancels a blocked read call. This function is thread safe, i.e. it may be called from a thread even +/** + * Cancels a blocked read call. This function is thread safe, i.e. it may be called from a thread even * while another thread is blocked in a read call. * @param serial_config the serial port to interrupt * @return 0 on success - * @return E_IO on error */ + * @return -E_IO on error + */ int serial_cancel_read(struct serial_config* const serial); -/**Writes data to a previously opened serial port. Non bocking. +/** + * Writes data to a previously opened serial port. Non bocking. * @param serial pointer to serial configuration to which to write * @param data data to write * @param size number of bytes to write from data * @return n>0 the number of bytes written - * @return E_IO on IO error */ + * @return -E_IO on IO error + */ int serial_write(struct serial_config* const serial, char* const data, size_t size); -/**Sets debugging option. If debugging is enabled, detailed error message are printed from method calls. */ +/** + * Sets debugging option. If debugging is enabled, detailed error message are printed from method calls. + */ void serial_debug(bool value); #ifdef __cplusplus } #endif - #endif /* FLOW_H */ diff --git a/flow-native/src/platform/posix/flow.c b/flow-native/src/platform/posix/flow.c index a2a239c..969949b 100644 --- a/flow-native/src/platform/posix/flow.c +++ b/flow-native/src/platform/posix/flow.c @@ -5,64 +5,77 @@ #include <termios.h> #include <fcntl.h> #include <sys/file.h> +#include <sys/select.h> #include "flow.h" - #define DATA_CANCEL 0xffffffff static bool debug = false; -#define DEBUG(f) if (debug) {f} -void serial_debug(bool value) { - debug = value; +static void print_debug(const char* const msg, int en) +{ + if (debug) { + if (errno == 0) { + fprintf(stderr, "%s", msg); + } else { + fprintf(stderr, "%s: %d\n", msg, en); + } + fflush(stderr); + } +} + +void serial_debug(bool value) +{ + debug = value; } //contains file descriptors used in managing a serial port struct serial_config { - int port_fd; // file descriptor of serial port + int port_fd; // file descriptor of serial port - /* a pipe is used to abort a serial read by writing something into the - * write end of the pipe */ - int pipe_read_fd; // file descriptor, read end of pipe - int pipe_write_fd; // file descriptor, write end of pipe + /* a pipe is used to abort a serial read by writing something into the + * write end of the pipe */ + int pipe_read_fd; // file descriptor, read end of pipe + int pipe_write_fd; // file descriptor, write end of pipe }; int serial_open( - const char* const port_name, - int baud, - int char_size, - bool two_stop_bits, - int parity, - struct serial_config** serial) { - - int fd = open(port_name, O_RDWR | O_NOCTTY | O_NONBLOCK); - - if (fd < 0) { - int en = errno; - DEBUG(perror("error obtaining port file descriptor");); - if (en == EACCES) return E_ACCESS_DENIED; - if (en == ENOENT) return E_NO_PORT; - return E_IO; - } - - if (flock(fd, LOCK_EX | LOCK_NB) < 0) { - DEBUG(perror("error acquiring lock on port");); - close(fd); - return E_BUSY; - } - - /* configure new port settings */ - struct termios newtio; - - /* initialize serial interface */ - newtio.c_iflag = 0; - newtio.c_oflag = 0; - newtio.c_lflag = 0; - newtio.c_cflag = CREAD; - - /* set speed */ - speed_t bd; - switch (baud) { + const char* const port_name, + int baud, + int char_size, + bool two_stop_bits, + int parity, + struct serial_config** serial) +{ + + int fd = open(port_name, O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (fd < 0) { + int en = errno; + print_debug("Error obtaining file descriptor for port", en); + if (en == EACCES) return -E_ACCESS_DENIED; + if (en == ENOENT) return -E_NO_PORT; + return -E_IO; + } + + if (flock(fd, LOCK_EX | LOCK_NB) < 0) { + print_debug("Error acquiring lock on port", errno); + close(fd); + return -E_BUSY; + } + + /* configure new port settings */ + struct termios newtio; + + /* initialize serial interface */ + newtio.c_iflag = 0; + newtio.c_oflag = 0; + newtio.c_lflag = 0; + newtio.c_cflag = CREAD; + + /* set speed */ + speed_t bd; + switch (baud) { case 50: bd = B50; break; case 75: bd = B75; break; case 110: bd = B110; break; @@ -82,166 +95,169 @@ int serial_open( case 115200: bd = B115200; break; case 230400: bd = B230400; break; default: - close(fd); - DEBUG(fprintf(stderr, "invalid baud rate %d\n", baud);); - return E_INVALID_SETTINGS; - } - - if (cfsetspeed(&newtio, bd) < 0) { - DEBUG(perror("error setting baud rate");); - close(fd); - return E_IO; - } - - /* set char size*/ - switch (char_size) { + close(fd); + print_debug("Invalid baud rate", 0); + return -E_INVALID_SETTINGS; + } + + if (cfsetspeed(&newtio, bd) < 0) { + print_debug("Error setting baud rate", errno); + close(fd); + return -E_IO; + } + + /* set char size*/ + switch (char_size) { case 5: newtio.c_cflag |= CS5; break; case 6: newtio.c_cflag |= CS6; break; case 7: newtio.c_cflag |= CS7; break; case 8: newtio.c_cflag |= CS8; break; default: - close(fd); - DEBUG(fprintf(stderr, "invalid character size %d\n", char_size);); - return E_INVALID_SETTINGS; - } - - /* use two stop bits */ - if (two_stop_bits){ - newtio.c_cflag |= CSTOPB; - } - - /* set parity */ - switch (parity) { + close(fd); + print_debug("Invalid character size", 0); + return -E_INVALID_SETTINGS; + } + + /* use two stop bits */ + if (two_stop_bits){ + newtio.c_cflag |= CSTOPB; + } + + /* set parity */ + switch (parity) { case PARITY_NONE: break; case PARITY_ODD: newtio.c_cflag |= (PARENB | PARODD); break; case PARITY_EVEN: newtio.c_cflag |= PARENB; break; default: - close(fd); - DEBUG(fprintf(stderr, "invalid parity %d\n", parity);); - return E_INVALID_SETTINGS; - } - - if (tcflush(fd, TCIOFLUSH) < 0) { - DEBUG(perror("error flushing serial settings");); - close(fd); - return E_IO; - } - - if (tcsetattr(fd, TCSANOW, &newtio) < 0) { - DEBUG(perror("error applying serial settings");); - close(fd); - return E_IO; - } - - int pipe_fd[2]; - if (pipe(pipe_fd) < 0) { - DEBUG(perror("error opening pipe");); - close(fd); - return E_IO; - } - - if (fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK) < 0 || fcntl(pipe_fd[1], F_SETFL, O_NONBLOCK) < 0) { - DEBUG(perror("error setting pipe to non-blocking");); - close(fd); - close(pipe_fd[0]); - close(pipe_fd[1]); - return E_IO; - } - - struct serial_config* s = malloc(sizeof(s)); - if (s == NULL) { - DEBUG(perror("error allocating memory for serial configuration");); - close(fd); - close(pipe_fd[0]); - close(pipe_fd[1]); - return E_IO; - } - - s->port_fd = fd; - s->pipe_read_fd = pipe_fd[0]; - s->pipe_write_fd = pipe_fd[1]; - (*serial) = s; - - return 0; + close(fd); + print_debug("Invalid parity", 0); + return -E_INVALID_SETTINGS; + } + + if (tcflush(fd, TCIOFLUSH) < 0) { + print_debug("Error flushing serial settings", errno); + close(fd); + return -E_IO; + } + + if (tcsetattr(fd, TCSANOW, &newtio) < 0) { + print_debug("Error applying serial settings", errno); + close(fd); + return -E_IO; + } + + int pipe_fd[2]; + if (pipe(pipe_fd) < 0) { + print_debug("Error opening pipe", errno); + close(fd); + return -E_IO; + } + + if (fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK) < 0 || fcntl(pipe_fd[1], F_SETFL, O_NONBLOCK) < 0) { + print_debug("Error setting pipe to non-blocking", errno); + close(fd); + close(pipe_fd[0]); + close(pipe_fd[1]); + return -E_IO; + } + + struct serial_config* s = malloc(sizeof(s)); + if (s == NULL) { + print_debug("Error allocating memory for serial configuration", errno); + close(fd); + close(pipe_fd[0]); + close(pipe_fd[1]); + return -E_IO; + } + + s->port_fd = fd; + s->pipe_read_fd = pipe_fd[0]; + s->pipe_write_fd = pipe_fd[1]; + (*serial) = s; + + return 0; } +int serial_close(struct serial_config* const serial) +{ + if (close(serial->pipe_write_fd) < 0) { + print_debug("Error closing write end of pipe", errno); + return -E_IO; + } + if (close(serial->pipe_read_fd) < 0) { + print_debug("Error closing read end of pipe", errno); + return -E_IO; + } + + if (flock(serial->port_fd, LOCK_UN) < 0){ + print_debug("Error releasing lock on port", errno); + return -E_IO; + } + if (close(serial->port_fd) < 0) { + print_debug("Error closing port", errno); + return -E_IO; + } -int serial_close(struct serial_config* const serial) { - if (close(serial->pipe_write_fd) < 0) { - DEBUG(perror("error closing write end of pipe");); - return E_IO; - } - if (close(serial->pipe_read_fd) < 0) { - DEBUG(perror("error closing read end of pipe");); - return E_IO; - } - - if (flock(serial->port_fd, LOCK_UN) < 0){ - DEBUG(perror("error releasing lock on port");); - return E_IO; - } - if (close(serial->port_fd) < 0) { - DEBUG(perror("error closing port");); - return E_IO; - } - - free(serial); - return 0; + free(serial); + return 0; } -int serial_read(struct serial_config* const serial, char* const buffer, size_t size) { - int port = serial->port_fd; - int pipe = serial->pipe_read_fd; - - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(port, &rfds); - FD_SET(pipe, &rfds); - - int nfds = pipe + 1; - if (pipe < port) nfds = port + 1; - - int n = select(nfds, &rfds, NULL, NULL, NULL); - if (n < 0) { - DEBUG(perror("error trying to call select on port and pipe");); - return E_IO; - } - - if (FD_ISSET(pipe, &rfds)) { - return E_INTERRUPT; - } else if (FD_ISSET(port, &rfds)) { - int r = read(port, buffer, size); - - //treat 0 bytes read as an error to avoid problems on disconnect - //anyway, after a poll there should be more than 0 bytes available to read - if (r <= 0) { - DEBUG(perror("error data not available after select");); - return E_IO; - } - return r; - } else { - fprintf(stderr, "select returned unknown read sets\n"); - return E_IO; - } +int serial_read(struct serial_config* const serial, char* const buffer, size_t size) +{ + int port = serial->port_fd; + int pipe = serial->pipe_read_fd; + + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(port, &rfds); + FD_SET(pipe, &rfds); + + int nfds = pipe + 1; + if (pipe < port) nfds = port + 1; + + int n = select(nfds, &rfds, NULL, NULL, NULL); + if (n < 0) { + print_debug("Error trying to call select on port and pipe", errno); + return -E_IO; + } + + if (FD_ISSET(pipe, &rfds)) { + return -E_INTERRUPT; + } else if (FD_ISSET(port, &rfds)) { + int r = read(port, buffer, size); + + // treat 0 bytes read as an error to avoid problems on disconnect + // anyway, after a poll there should be more than 0 bytes available to read + if (r <= 0) { + print_debug("Error data not available after select", errno); + return -E_IO; + } + return r; + } else { + print_debug("Select returned unknown read sets", 0); + return -E_IO; + } } -int serial_cancel_read(struct serial_config* const serial) { - int data = DATA_CANCEL; - - //write to pipe to wake up any blocked read thread (self-pipe trick) - if (write(serial->pipe_write_fd, &data, 1) < 0) { - DEBUG(perror("error writing to pipe during read cancel");); - return E_IO; - } - - return 0; +int serial_cancel_read(struct serial_config* const serial) +{ + int data = DATA_CANCEL; + + //write to pipe to wake up any blocked read thread (self-pipe trick) + if (write(serial->pipe_write_fd, &data, 1) < 0) { + print_debug("Error writing to pipe during read cancel", errno); + return -E_IO; + } + + return 0; } -int serial_write(struct serial_config* const serial, char* const data, size_t size) { - int r = write(serial->port_fd, data, size); - if (r < 0) { - DEBUG(perror("error writing to port");); - return E_IO; - } - return r; -}
\ No newline at end of file +int serial_write(struct serial_config* const serial, char* const data, size_t size) +{ + int r = write(serial->port_fd, data, size); + if (r < 0) { + print_debug("Error writing to port", errno); + return -E_IO; + } + return r; +} |