aboutsummaryrefslogtreecommitdiff
path: root/native
diff options
context:
space:
mode:
authorJakob Odersky <jakob@odersky.com>2017-01-08 21:16:25 +0100
committerJakob Odersky <jakob@odersky.com>2017-01-21 17:22:10 -0800
commit23959966760174477a6b0fcbf9dd1e8ef37c643b (patch)
tree9a0ee44eb43a8c13af57b0d06313f3aabf9e4555 /native
parent6c371ba6d69c891c1f0d6df00bb643e1d543cc9d (diff)
downloadakka-serial-23959966760174477a6b0fcbf9dd1e8ef37c643b.tar.gz
akka-serial-23959966760174477a6b0fcbf9dd1e8ef37c643b.tar.bz2
akka-serial-23959966760174477a6b0fcbf9dd1e8ef37c643b.zip
Rename project to akka-serial
Diffstat (limited to 'native')
-rw-r--r--native/build.sbt8
-rw-r--r--native/src/.gitignore12
-rw-r--r--native/src/CMakeLists.txt46
-rw-r--r--native/src/akka_serial_jni.c150
-rw-r--r--native/src/include/akka_serial.h103
-rw-r--r--native/src/include/akka_serial_sync_UnsafeSerial.h45
-rw-r--r--native/src/include/akka_serial_sync_UnsafeSerial__.h29
-rw-r--r--native/src/platform/posix/akka_serial.c263
-rw-r--r--native/src/platform/windows/README1
-rw-r--r--native/src/platform/windows/akka_serial.c.disabled416
-rw-r--r--native/src/readme.md3
11 files changed, 1076 insertions, 0 deletions
diff --git a/native/build.sbt b/native/build.sbt
new file mode 100644
index 0000000..2c7ffea
--- /dev/null
+++ b/native/build.sbt
@@ -0,0 +1,8 @@
+enablePlugins(JniNative)
+
+sourceDirectory in nativeCompile := sourceDirectory.value
+
+// package native libraries from lib_native during releases
+val isRelease = sys.props("release") == "true"
+enableNativeCompilation in Compile := !isRelease
+enableNativeCompilation in Test := !isRelease
diff --git a/native/src/.gitignore b/native/src/.gitignore
new file mode 100644
index 0000000..1785c46
--- /dev/null
+++ b/native/src/.gitignore
@@ -0,0 +1,12 @@
+# CMake
+/CMakeFiles/
+/CMakeCache.txt
+/cmake_install.cmake
+/Makefile
+
+# Binary files
+*.o
+*.so*
+*.dylib
+*.a
+*~ \ No newline at end of file
diff --git a/native/src/CMakeLists.txt b/native/src/CMakeLists.txt
new file mode 100644
index 0000000..a6b037d
--- /dev/null
+++ b/native/src/CMakeLists.txt
@@ -0,0 +1,46 @@
+################################################################
+# 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)
+set(ignoreMe "${SBT}") # sbt-jni defines -DSBT
+
+# Define project and related variables
+# (required by sbt-jni) please use semantic versioning
+#
+project (akkaserial)
+set(PROJECT_VERSION_MAJOR 1)
+set(PROJECT_VERSION_MINOR 0)
+set(PROJECT_VERSION_PATCH 0)
+
+set(CMAKE_C_FLAGS "-std=c99")
+add_definitions(-Wall)
+add_definitions(-Wextra)
+add_definitions(-pedantic)
+
+# Setup JNI
+find_package(JNI REQUIRED)
+if (JNI_FOUND)
+ message (STATUS "JNI include directories: ${JNI_INCLUDE_DIRS}")
+endif()
+
+# Include directories
+include_directories(.)
+include_directories(include)
+include_directories(${JNI_INCLUDE_DIRS})
+
+# Sources
+file(GLOB LIB_SRC
+ "*.c"
+ "platform/posix/*.c"
+)
+
+# 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/native/src/akka_serial_jni.c b/native/src/akka_serial_jni.c
new file mode 100644
index 0000000..126a7bf
--- /dev/null
+++ b/native/src/akka_serial_jni.c
@@ -0,0 +1,150 @@
+#include <stdint.h>
+
+#include "akka_serial.h"
+
+#include "akka_serial_sync_UnsafeSerial.h"
+#include "akka_serial_sync_UnsafeSerial__.h"
+
+// suppress unused parameter warnings
+#define UNUSED_ARG(x) (void)(x)
+
+static inline void throwException(JNIEnv* env, const char* const exception, const char * const message)
+{
+ (*env)->ThrowNew(env, (*env)->FindClass(env, exception), message);
+}
+
+/** 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, "akka/serial/PortInUseException", ""); break;
+ case -E_ACCESS_DENIED: throwException(env, "akka/serial/AccessDeniedException", ""); break;
+ case -E_INVALID_SETTINGS: throwException(env, "akka/serial/InvalidSettingsException", ""); break;
+ case -E_INTERRUPT: throwException(env, "akka/serial/PortInterruptedException", ""); break;
+ case -E_NO_PORT: throwException(env, "akka/serial/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, "akka/serial/sync/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: akka_serial_sync_UnsafeSerial__
+ * Method: open
+ * Signature: (Ljava/lang/String;IIZI)J
+ */
+JNIEXPORT jlong JNICALL Java_akka_serial_sync_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: akka_serial_sync_UnsafeSerial
+ * Method: read
+ * Signature: (Ljava/nio/ByteBuffer;)I
+ */
+JNIEXPORT jint JNICALL Java_akka_serial_sync_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: akka_serial_sync_UnsafeSerial
+ * Method: cancelRead
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_akka_serial_sync_UnsafeSerial_cancelRead
+(JNIEnv *env, jobject instance)
+{
+ int r = serial_cancel_read(get_config(env, instance));
+ if (r < 0) {
+ check(env, r);
+ }
+}
+
+/*
+ * Class: akka_serial_sync_UnsafeSerial
+ * Method: write
+ * Signature: (Ljava/nio/ByteBuffer;I)I
+ */
+JNIEXPORT jint JNICALL Java_akka_serial_sync_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: akka_serial_sync_UnsafeSerial
+ * Method: close
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_akka_serial_sync_UnsafeSerial_close
+(JNIEnv *env, jobject instance)
+{
+ int r = serial_close(get_config(env, instance));
+ if (r < 0) {
+ check(env, r);
+ }
+}
+
+/*
+ * Class: akka_serial_sync_UnsafeSerial__
+ * Method: debug
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_akka_serial_sync_UnsafeSerial_00024_debug
+(JNIEnv *env, jobject instance, jboolean value)
+{
+ UNUSED_ARG(env);
+ UNUSED_ARG(instance);
+
+ serial_debug((bool) value);
+}
diff --git a/native/src/include/akka_serial.h b/native/src/include/akka_serial.h
new file mode 100644
index 0000000..23507a9
--- /dev/null
+++ b/native/src/include/akka_serial.h
@@ -0,0 +1,103 @@
+#ifndef AKKA_SERIAL_H
+#define AKKA_SERIAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stddef.h>
+
+// 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.
+ */
+struct serial_config;
+
+/**
+ * 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
+ * @param char_size character size of data transmitted through serial device
+ * @param two_stop_bits set to use two stop bits instead of one
+ * @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
+ */
+int serial_open(
+ 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
+ * 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).
+ * @param serial pointer to serial configuration that is to be closed (and freed)
+ * @return 0 on success
+ * @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
+ * 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
+ */
+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
+ * 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
+ */
+int serial_cancel_read(struct serial_config* const serial);
+
+/**
+ * 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
+ */
+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.
+ */
+void serial_debug(bool value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AKKA_SERIAL_H */
diff --git a/native/src/include/akka_serial_sync_UnsafeSerial.h b/native/src/include/akka_serial_sync_UnsafeSerial.h
new file mode 100644
index 0000000..893ccf6
--- /dev/null
+++ b/native/src/include/akka_serial_sync_UnsafeSerial.h
@@ -0,0 +1,45 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class akka_serial_sync_UnsafeSerial */
+
+#ifndef _Included_akka_serial_sync_UnsafeSerial
+#define _Included_akka_serial_sync_UnsafeSerial
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: akka_serial_sync_UnsafeSerial
+ * Method: read
+ * Signature: (Ljava/nio/ByteBuffer;)I
+ */
+JNIEXPORT jint JNICALL Java_akka_serial_sync_UnsafeSerial_read
+ (JNIEnv *, jobject, jobject);
+
+/*
+ * Class: akka_serial_sync_UnsafeSerial
+ * Method: cancelRead
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_akka_serial_sync_UnsafeSerial_cancelRead
+ (JNIEnv *, jobject);
+
+/*
+ * Class: akka_serial_sync_UnsafeSerial
+ * Method: write
+ * Signature: (Ljava/nio/ByteBuffer;I)I
+ */
+JNIEXPORT jint JNICALL Java_akka_serial_sync_UnsafeSerial_write
+ (JNIEnv *, jobject, jobject, jint);
+
+/*
+ * Class: akka_serial_sync_UnsafeSerial
+ * Method: close
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_akka_serial_sync_UnsafeSerial_close
+ (JNIEnv *, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/native/src/include/akka_serial_sync_UnsafeSerial__.h b/native/src/include/akka_serial_sync_UnsafeSerial__.h
new file mode 100644
index 0000000..5f4bdb9
--- /dev/null
+++ b/native/src/include/akka_serial_sync_UnsafeSerial__.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class akka_serial_sync_UnsafeSerial__ */
+
+#ifndef _Included_akka_serial_sync_UnsafeSerial__
+#define _Included_akka_serial_sync_UnsafeSerial__
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: akka_serial_sync_UnsafeSerial__
+ * Method: open
+ * Signature: (Ljava/lang/String;IIZI)J
+ */
+JNIEXPORT jlong JNICALL Java_akka_serial_sync_UnsafeSerial_00024_open
+ (JNIEnv *, jobject, jstring, jint, jint, jboolean, jint);
+
+/*
+ * Class: akka_serial_sync_UnsafeSerial__
+ * Method: debug
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_akka_serial_sync_UnsafeSerial_00024_debug
+ (JNIEnv *, jobject, jboolean);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/native/src/platform/posix/akka_serial.c b/native/src/platform/posix/akka_serial.c
new file mode 100644
index 0000000..a2de056
--- /dev/null
+++ b/native/src/platform/posix/akka_serial.c
@@ -0,0 +1,263 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include "akka_serial.h"
+
+#define DATA_CANCEL 0xffffffff
+
+static bool debug = false;
+
+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
+
+ /* 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;
+ 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;
+ case 134: bd = B134; break;
+ case 150: bd = B150; break;
+ case 200: bd = B200; break;
+ case 300: bd = B300; break;
+ case 600: bd = B600; break;
+ case 1200: bd = B1200; break;
+ case 1800: bd = B1800; break;
+ case 2400: bd = B2400; break;
+ case 4800: bd = B4800; break;
+ case 9600: bd = B9600; break;
+ case 19200: bd = B19200; break;
+ case 38400: bd = B38400; break;
+ case 57600: bd = B57600; break;
+ case 115200: bd = B115200; break;
+ case 230400: bd = B230400; break;
+ default:
+ 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);
+ 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);
+ 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;
+ }
+
+ 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) {
+ 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) {
+ 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) {
+ print_debug("Error writing to port", errno);
+ return -E_IO;
+ }
+ return r;
+}
diff --git a/native/src/platform/windows/README b/native/src/platform/windows/README
new file mode 100644
index 0000000..ea7c736
--- /dev/null
+++ b/native/src/platform/windows/README
@@ -0,0 +1 @@
+The contents of the file akka_serial.c were found in the avrdude project. They look like they may be a good starting point for serial communication on windows.
diff --git a/native/src/platform/windows/akka_serial.c.disabled b/native/src/platform/windows/akka_serial.c.disabled
new file mode 100644
index 0000000..86a267c
--- /dev/null
+++ b/native/src/platform/windows/akka_serial.c.disabled
@@ -0,0 +1,416 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2003, 2004 Martin J. Thomas <mthomas@rhrk.uni-kl.de>
+ * Copyright (C) 2006 Joerg Wunsch <j@uriah.heep.sax.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* $Id$ */
+
+/*
+ * Native Win32 serial interface for avrdude.
+ */
+
+#include "avrdude.h"
+
+#if defined(WIN32NATIVE)
+
+#include <windows.h>
+#include <stdio.h>
+#include <ctype.h> /* for isprint */
+
+#include "serial.h"
+
+long serial_recv_timeout = 5000; /* ms */
+
+#define W32SERBUFSIZE 1024
+
+struct baud_mapping {
+ long baud;
+ DWORD speed;
+};
+
+/* HANDLE hComPort=INVALID_HANDLE_VALUE; */
+
+static struct baud_mapping baud_lookup_table [] = {
+ { 1200, CBR_1200 },
+ { 2400, CBR_2400 },
+ { 4800, CBR_4800 },
+ { 9600, CBR_9600 },
+ { 19200, CBR_19200 },
+ { 38400, CBR_38400 },
+ { 57600, CBR_57600 },
+ { 115200, CBR_115200 },
+ { 0, 0 } /* Terminator. */
+};
+
+static DWORD serial_baud_lookup(long baud)
+{
+ struct baud_mapping *map = baud_lookup_table;
+
+ while (map->baud) {
+ if (map->baud == baud)
+ return map->speed;
+ map++;
+ }
+
+ /*
+ * If a non-standard BAUD rate is used, issue
+ * a warning (if we are verbose) and return the raw rate
+ */
+ if (verbose > 0)
+ fprintf(stderr, "%s: serial_baud_lookup(): Using non-standard baud rate: %ld",
+ progname, baud);
+
+ return baud;
+}
+
+
+static BOOL serial_w32SetTimeOut(HANDLE hComPort, DWORD timeout) // in ms
+{
+ COMMTIMEOUTS ctmo;
+ ZeroMemory (&ctmo, sizeof(COMMTIMEOUTS));
+ ctmo.ReadIntervalTimeout = timeout;
+ ctmo.ReadTotalTimeoutMultiplier = timeout;
+ ctmo.ReadTotalTimeoutConstant = timeout;
+
+ return SetCommTimeouts(hComPort, &ctmo);
+}
+
+static int ser_setspeed(union filedescriptor *fd, long baud)
+{
+ DCB dcb;
+ HANDLE hComPort = (HANDLE)fd->pfd;
+
+ ZeroMemory (&dcb, sizeof(DCB));
+ dcb.DCBlength = sizeof(DCB);
+ dcb.BaudRate = serial_baud_lookup (baud);
+ dcb.fBinary = 1;
+ dcb.fDtrControl = DTR_CONTROL_DISABLE;
+ dcb.fRtsControl = RTS_CONTROL_DISABLE;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+
+ if (!SetCommState(hComPort, &dcb))
+ return -1;
+
+ return 0;
+}
+
+
+static int ser_open(char * port, long baud, union filedescriptor *fdp)
+{
+ LPVOID lpMsgBuf;
+ HANDLE hComPort=INVALID_HANDLE_VALUE;
+ char *newname = 0;
+
+ /*
+ * If the port is of the form "net:<host>:<port>", then
+ * handle it as a TCP connection to a terminal server.
+ *
+ * This is curently not implemented for Win32.
+ */
+ if (strncmp(port, "net:", strlen("net:")) == 0) {
+ fprintf(stderr,
+ "%s: ser_open(): network connects are currently not"
+ "implemented for Win32 environments\n",
+ progname);
+ return -1;
+ }
+
+ if (strncasecmp(port, "com", strlen("com")) == 0) {
+
+ // prepend "\\\\.\\" to name, required for port # >= 10
+ newname = malloc(strlen("\\\\.\\") + strlen(port) + 1);
+
+ if (newname == 0) {
+ fprintf(stderr,
+ "%s: ser_open(): out of memory\n",
+ progname);
+ exit(1);
+ }
+ strcpy(newname, "\\\\.\\");
+ strcat(newname, port);
+
+ port = newname;
+ }
+
+ hComPort = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hComPort == INVALID_HANDLE_VALUE) {
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL);
+ fprintf(stderr, "%s: ser_open(): can't open device \"%s\": %s\n",
+ progname, port, (char*)lpMsgBuf);
+ LocalFree( lpMsgBuf );
+ return -1;
+ }
+
+ if (!SetupComm(hComPort, W32SERBUFSIZE, W32SERBUFSIZE))
+ {
+ CloseHandle(hComPort);
+ fprintf(stderr, "%s: ser_open(): can't set buffers for \"%s\"\n",
+ progname, port);
+ return -1;
+ }
+
+ fdp->pfd = (void *)hComPort;
+ if (ser_setspeed(fdp, baud) != 0)
+ {
+ CloseHandle(hComPort);
+ fprintf(stderr, "%s: ser_open(): can't set com-state for \"%s\"\n",
+ progname, port);
+ return -1;
+ }
+
+ if (!serial_w32SetTimeOut(hComPort,0))
+ {
+ CloseHandle(hComPort);
+ fprintf(stderr, "%s: ser_open(): can't set initial timeout for \"%s\"\n",
+ progname, port);
+ return -1;
+ }
+
+ if (newname != 0) {
+ free(newname);
+ }
+ return 0;
+}
+
+
+static void ser_close(union filedescriptor *fd)
+{
+ HANDLE hComPort=(HANDLE)fd->pfd;
+ if (hComPort != INVALID_HANDLE_VALUE)
+ CloseHandle (hComPort);
+
+ hComPort = INVALID_HANDLE_VALUE;
+}
+
+static int ser_set_dtr_rts(union filedescriptor *fd, int is_on)
+{
+ HANDLE hComPort=(HANDLE)fd->pfd;
+
+ if (is_on) {
+ EscapeCommFunction(hComPort, SETDTR);
+ EscapeCommFunction(hComPort, SETRTS);
+ } else {
+ EscapeCommFunction(hComPort, CLRDTR);
+ EscapeCommFunction(hComPort, CLRRTS);
+ }
+ return 0;
+}
+
+
+static int ser_send(union filedescriptor *fd, unsigned char * buf, size_t buflen)
+{
+ size_t len = buflen;
+ unsigned char c='\0';
+ DWORD written;
+ unsigned char * b = buf;
+
+ HANDLE hComPort=(HANDLE)fd->pfd;
+
+ if (hComPort == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "%s: ser_send(): port not open\n",
+ progname);
+ exit(1);
+ }
+
+ if (!len)
+ return 0;
+
+ if (verbose > 3)
+ {
+ fprintf(stderr, "%s: Send: ", progname);
+
+ while (len) {
+ c = *b;
+ if (isprint(c)) {
+ fprintf(stderr, "%c ", c);
+ }
+ else {
+ fprintf(stderr, ". ");
+ }
+ fprintf(stderr, "[%02x] ", c);
+ b++;
+ len--;
+ }
+ fprintf(stderr, "\n");
+ }
+
+ serial_w32SetTimeOut(hComPort,500);
+
+ if (!WriteFile (hComPort, buf, buflen, &written, NULL)) {
+ fprintf(stderr, "%s: ser_send(): write error: %s\n",
+ progname, "sorry no info avail"); // TODO
+ exit(1);
+ }
+
+ if (written != buflen) {
+ fprintf(stderr, "%s: ser_send(): size/send mismatch\n",
+ progname);
+ exit(1);
+ }
+
+ return 0;
+}
+
+
+static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen)
+{
+ unsigned char c;
+ unsigned char * p = buf;
+ DWORD read;
+
+ HANDLE hComPort=(HANDLE)fd->pfd;
+
+ if (hComPort == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "%s: ser_read(): port not open\n",
+ progname);
+ exit(1);
+ }
+
+ serial_w32SetTimeOut(hComPort, serial_recv_timeout);
+
+ if (!ReadFile(hComPort, buf, buflen, &read, NULL)) {
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL );
+ fprintf(stderr, "%s: ser_recv(): read error: %s\n",
+ progname, (char*)lpMsgBuf);
+ LocalFree( lpMsgBuf );
+ exit(1);
+ }
+
+ /* time out detected */
+ if (read == 0) {
+ if (verbose > 1)
+ fprintf(stderr,
+ "%s: ser_recv(): programmer is not responding\n",
+ progname);
+ return -1;
+ }
+
+ p = buf;
+
+ if (verbose > 3)
+ {
+ fprintf(stderr, "%s: Recv: ", progname);
+
+ while (read) {
+ c = *p;
+ if (isprint(c)) {
+ fprintf(stderr, "%c ", c);
+ }
+ else {
+ fprintf(stderr, ". ");
+ }
+ fprintf(stderr, "[%02x] ", c);
+
+ p++;
+ read--;
+ }
+ fprintf(stderr, "\n");
+ }
+ return 0;
+}
+
+
+static int ser_drain(union filedescriptor *fd, int display)
+{
+ // int rc;
+ unsigned char buf[10];
+ BOOL readres;
+ DWORD read;
+
+ HANDLE hComPort=(HANDLE)fd->pfd;
+
+ if (hComPort == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "%s: ser_drain(): port not open\n",
+ progname);
+ exit(1);
+ }
+
+ serial_w32SetTimeOut(hComPort,250);
+
+ if (display) {
+ fprintf(stderr, "drain>");
+ }
+
+ while (1) {
+ readres=ReadFile(hComPort, buf, 1, &read, NULL);
+ if (!readres) {
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL );
+ fprintf(stderr, "%s: ser_drain(): read error: %s\n",
+ progname, (char*)lpMsgBuf);
+ LocalFree( lpMsgBuf );
+ exit(1);
+ }
+
+ if (read) { // data avail
+ if (display) fprintf(stderr, "%02x ", buf[0]);
+ }
+ else { // no more data
+ if (display) fprintf(stderr, "<drain\n");
+ break;
+ }
+ } // while
+ return 0;
+}
+
+struct serial_device serial_serdev =
+{
+ .open = ser_open,
+ .setspeed = ser_setspeed,
+ .close = ser_close,
+ .send = ser_send,
+ .recv = ser_recv,
+ .drain = ser_drain,
+ .set_dtr_rts = ser_set_dtr_rts,
+ .flags = SERDEV_FL_CANSETSPEED,
+};
+
+struct serial_device *serdev = &serial_serdev;
+
+#endif /* WIN32NATIVE */
diff --git a/native/src/readme.md b/native/src/readme.md
new file mode 100644
index 0000000..9b9d00d
--- /dev/null
+++ b/native/src/readme.md
@@ -0,0 +1,3 @@
+# Native backend for akka-serial
+
+Refer to [developer.md](../../site/jekyll/documentation/developer.md) for information on how to build.