Subversion Repositories Programming Utils

Rev

Rev 96 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#ifdef _WIN32
        #include <windows.h>
       
        /* Make inline functions work */
        #define inline __inline
       
        #define SPEED_SWITCH(SPD,io) case SPD: io.BaudRate = CBR_##SPD; break;
        #define GET_SPEED_SWITCH(SPD,io) case CBR_##SPD: return SPD;
       
        #define GET_SERIAL_PORT_STRUCT( port, io_name ) DCB io_name = {0};\
                                                                                                io_name.DCBlength = sizeof( io_name ); \
                                                                                                if (!GetCommState( port, &io_name ) ) { \
                                                                                                printf("bad get comm\n");\
                                                                                                        return -1;\
                                                                                                }

        #define SET_SERIAL_PORT_STRUCT( port, io_name )         if( !SetCommState( port, &io_name ) ){\
        printf("bad set comm\n");\
                                                                                                                        return 0;\
                                                                                                                }

                                                                                                               
        #define close(handle) CloseHandle(handle)
#else
        #include <termios.h>
        #include <unistd.h>
        #include <fcntl.h>
        #include <unistd.h>
        #include <dirent.h>
        #include <pthread.h>
        #include <sys/ioctl.h>
        #include <errno.h>

        #ifndef ENOMEDIUM
        #define ENOMEDIUM ENODEV
        #endif

        #ifdef CRTSCTS
                #define HW_FLOW CRTSCTS
        #elif CNEW_RTSCTS
                #define HW_FLOW CNEW_RTSCTS
        #endif
       
        #define SPEED_SWITCH(SPD,io) case SPD: cfsetospeed( &io, B##SPD ); cfsetispeed( &io, B##SPD ); break;
        #define GET_SPEED_SWITCH(SPD,io) case B##SPD: return SPD;
       
       
        #define GET_SERIAL_PORT_STRUCT( port, io_name ) struct termios io_name; \
                                                                                                        if( tcgetattr( port, &io_name ) < 0 ){ \
                                                                                                                return -1; \
                                                                                                        }

        #define SET_SERIAL_PORT_STRUCT( port, io_name )         if( tcsetattr( port, TCSANOW, &io_name ) < 0 ){\
                                                                                                                        return -1;\
                                                                                                                }

#endif

#include <stdlib.h>
#include <errno.h>
#include <string.h>

//
// Local Includes
//
#include "com_rm5248_serial_SerialPort.h"
#include "com_rm5248_serial_SerialInputStream.h"
#include "com_rm5248_serial_SerialOutputStream.h"
#include "com_rm5248_serial_SimpleSerialInputStream.h"

//
// Struct Definitions
//
struct port_descriptor{
#ifdef _WIN32
        HANDLE port;
#else
        int port;
        /* There appears to be a case where we can free() the port_descriptor
         * after the select() call in SerialInputStream_readByte().
         * This means that we segfault on the FD_SET() call.
         * This mutex will keep us alive until we have exited the
         * readByte() call.
         */

        pthread_mutex_t in_use;
#endif
};

//
// Local Variables
//
static struct port_descriptor** port_list = NULL;
static int port_list_size;

#ifdef _WIN32
//Unfortunately, Windows does not let us get the state of the DTR/RTS lines.
//So, we need to keep track of that manually.  
int winDTR = 0;
int winRTS = 0;
#endif

//
// Helper Methods
//
static inline jint get_handle(JNIEnv * env, jobject obj){
        jfieldID fid;
        jint array_pos;
        jclass cls = (*env)->GetObjectClass( env, obj );

        fid = (*env)->GetFieldID( env, cls, "handle", "I" );
        if( fid == 0 ){
                return -1;
        }

        array_pos = (*env)->GetIntField( env, obj, fid );

        return array_pos;
}

static inline jboolean get_bool( JNIEnv* env, jobject obj, const char* name ){
        jfieldID fid;
        jboolean boolVal;
        jclass cls = (*env)->GetObjectClass( env, obj );

        fid = (*env)->GetFieldID( env, cls, name, "Z" );
        if( fid == 0 ){
                return 0; //not really sure what error to set here...
        }

        boolVal = (*env)->GetBooleanField( env, obj, fid );

        return boolVal;
}

static inline int set_baud_rate( struct port_descriptor* desc, int baud_rate ){
        GET_SERIAL_PORT_STRUCT( desc->port, newio );

        switch( baud_rate ){
#ifndef _WIN32
/* Note that Windows only supports speeds of 110 and above */
                SPEED_SWITCH(0,newio);
                SPEED_SWITCH(50,newio);
                SPEED_SWITCH(75,newio);
#endif
                SPEED_SWITCH(110,newio);
#ifndef _WIN32
/* Windows does not support speeds of 134, 150, or 200 */
                SPEED_SWITCH(134,newio);
                SPEED_SWITCH(150,newio);
                SPEED_SWITCH(200,newio);
#endif
                SPEED_SWITCH(300,newio);
                SPEED_SWITCH(600,newio);
                SPEED_SWITCH(1200,newio);
#ifndef _WIN32
/* Windows does not support 1800 */
                SPEED_SWITCH(1800,newio);
#endif
                SPEED_SWITCH(2400,newio);
                SPEED_SWITCH(4800,newio);
                SPEED_SWITCH(9600,newio);
                SPEED_SWITCH(19200,newio);
                SPEED_SWITCH(38400,newio);
                SPEED_SWITCH(115200,newio);
        }

        SET_SERIAL_PORT_STRUCT( desc->port, newio );

        return 1;
}

static inline int set_raw_input( struct port_descriptor* desc ){
        GET_SERIAL_PORT_STRUCT( desc->port, newio );

#ifdef _WIN32
        newio.fBinary = TRUE;
        newio.fParity = TRUE;
        newio.fOutxCtsFlow = FALSE;
        newio.fOutxDsrFlow = FALSE;
        newio.fDtrControl = DTR_CONTROL_DISABLE;
        newio.fDsrSensitivity = FALSE;
        newio.fOutX = FALSE;
        newio.fInX = FALSE;
        newio.fNull = FALSE;
        newio.fRtsControl = FALSE;
       
        //Set the timeouts
        {
                COMMTIMEOUTS timeouts = {0};
                timeouts.ReadIntervalTimeout = MAXDWORD;
                timeouts.ReadTotalTimeoutMultiplier = 0;
                timeouts.ReadTotalTimeoutConstant = 0;
                timeouts.WriteTotalTimeoutMultiplier = 0;
                timeouts.WriteTotalTimeoutConstant = 0;
                if( SetCommTimeouts( desc->port, &timeouts ) == 0 ){
                printf("bad timeout\n"); fflush(stdout);}
        }
#else
        newio.c_iflag |= IGNBRK;
        newio.c_iflag &= ~BRKINT;
        newio.c_iflag &= ~ICRNL;
        newio.c_oflag = 0;
        newio.c_lflag = 0;
        newio.c_cc[VTIME] = 0;
        newio.c_cc[VMIN] = 1;
#endif
       
        SET_SERIAL_PORT_STRUCT( desc->port, newio );

        return 1;
}

/**
 * @param data_bits The number of data bits
 */

static inline int set_data_bits( struct port_descriptor* desc, int data_bits ){
        GET_SERIAL_PORT_STRUCT( desc->port, newio );

#ifdef _WIN32
        newio.ByteSize = data_bits;
#else
        newio.c_cflag &= ~CSIZE;
        if( data_bits == 8 ){
                newio.c_cflag |= CS8;
        }else if( data_bits == 7 ){
                newio.c_cflag |= CS7;
        }else if( data_bits == 6 ){
                newio.c_cflag |= CS6;
        }else if( data_bits == 5 ){
                newio.c_cflag |= CS5;
        }
#endif
       
        SET_SERIAL_PORT_STRUCT( desc->port, newio );

        return 1;
}

/**
 * @param stop_bits 1 for 1, 2 for 2
 */

static inline int set_stop_bits( struct port_descriptor* desc, int stop_bits ){
        GET_SERIAL_PORT_STRUCT( desc->port, newio );

#ifdef _WIN32
        if( stop_bits == 1 ){
                newio.StopBits = ONESTOPBIT;
        }else if( stop_bits == 2 ){
                newio.StopBits = TWOSTOPBITS;
        }
#else
        if( stop_bits == 1 ){
                newio.c_cflag &= ~CSTOPB;
        }else if( stop_bits == 2 ){
                newio.c_cflag |= CSTOPB;
        }
#endif
       
        SET_SERIAL_PORT_STRUCT( desc->port, newio );

        return 1;
}

/**
 * @param parity 0 for no parity, 1 for odd parity, 2 for even parity
 */

static inline int set_parity( struct port_descriptor* desc, int parity ){
        GET_SERIAL_PORT_STRUCT( desc->port, newio );

#ifdef _WIN32
        if( parity == 0 ){
                newio.Parity = NOPARITY;
        }else if( parity == 1 ){
                newio.Parity = ODDPARITY;
        }else if( parity == 2 ){
                newio.Parity = EVENPARITY;
        }
#else
        newio.c_iflag &= ~IGNPAR;
        newio.c_cflag &= ~( PARODD | PARENB );
        if( parity == 0 ){
                newio.c_iflag |= IGNPAR;
        }else if( parity == 1 ){
                newio.c_cflag |= PARODD;
        }else if( parity == 2 ){
                newio.c_cflag |= PARENB;
        }
#endif
       
        SET_SERIAL_PORT_STRUCT( desc->port, newio );

        return 1;
}

/**
 * @param flow_control 0 for none, 1 for hardware, 2 for software
 */

static inline int set_flow_control( struct port_descriptor* desc, int flow_control ){
        GET_SERIAL_PORT_STRUCT( desc->port, newio );

#ifdef _WIN32
        if( flow_control == 0 ){
                newio.fOutxCtsFlow = FALSE;
                newio.fRtsControl = FALSE;
                newio.fOutX = FALSE;
                newio.fInX = FALSE;
        }else if( flow_control == 1 ){
                newio.fOutxCtsFlow = TRUE;
                newio.fRtsControl = TRUE;
                newio.fOutX = FALSE;
                newio.fInX = FALSE;
        }else if( flow_control == 2 ){
                newio.fOutxCtsFlow = FALSE;
                newio.fRtsControl = FALSE;
                newio.fOutX = TRUE;
                newio.fInX = TRUE;
        }
#else
        newio.c_iflag &= ~( IXON | IXOFF | IXANY );
        newio.c_cflag &= ~HW_FLOW;
        if( flow_control == 0 ){
                newio.c_iflag &= ~( IXON | IXOFF | IXANY );
        }else if( flow_control == 1 ){
                newio.c_cflag |= HW_FLOW;
        }else if( flow_control == 2 ){
                newio.c_iflag |= ( IXON | IXOFF | IXANY );
        }
#endif
       
        SET_SERIAL_PORT_STRUCT( desc->port, newio );

        return 1;
}

static inline void throw_io_exception( JNIEnv * env, int errorNumber ){
#ifdef _WIN32
        LPTSTR error_text = NULL;
        jclass exception_class;
        (*env)->ExceptionDescribe( env );
        (*env)->ExceptionClear( env );
        exception_class = (*env)->FindClass(env, "java/io/IOException");
       
        FormatMessage(
                FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                errorNumber,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR)&error_text,
                0,
                NULL );
        (*env)->ThrowNew(env, exception_class, error_text );
        LocalFree( error_text );
#else
        jclass exception_class;
        (*env)->ExceptionDescribe( env );
        (*env)->ExceptionClear( env );
        exception_class = (*env)->FindClass(env, "java/io/IOException");
        (*env)->ThrowNew(env, exception_class, strerror( errorNumber ) );
#endif /* _WIN32 */
}

static inline void throw_io_exception_message( JNIEnv * env, const char* message ){
        jclass exception_class;
        (*env)->ExceptionDescribe( env );
        (*env)->ExceptionClear( env );
        exception_class = (*env)->FindClass(env, "java/io/IOException");
        (*env)->ThrowNew(env, exception_class, message );
}

static inline struct port_descriptor* get_port_descriptor( JNIEnv* env, jobject obj ){
        int array_pos;
        struct port_descriptor* desc;
       
        array_pos = get_handle( env, obj );
        if( array_pos < 0 || array_pos > port_list_size ){
                throw_io_exception_message( env, "Unable to get handle" );
                return NULL;
        }
        desc = port_list[ array_pos ];
        if( desc == NULL ){
                throw_io_exception_message( env, "Unable to get descriptor" );
                return NULL;
        }
       
        return desc;
}

//
// JNI Methods
//

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    openPort
 * Signature: (Ljava/lang/String;IIIII)I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_openPort
  (JNIEnv * env, jobject obj, jstring port, jint baudRate, jint dataBits, jint stopBits, jint parity, jint flowControl){
        struct port_descriptor* new_port;
        int list_pos;
        int found = 0;
        const char* port_to_open;
        jboolean iscopy;

        port_to_open = (*env)->GetStringUTFChars( env, port, &iscopy );

        if( port_list == NULL ){
                port_list = malloc( sizeof( struct port_descriptor* ) * 10 );
                port_list_size = 10;
                for( list_pos = 0; list_pos < port_list_size; ++list_pos ){
                        port_list[ list_pos ] = NULL;
                }
        }

        //Search thru the port_list, find the first one that is NULL
        for( list_pos = 0; list_pos < port_list_size; ++list_pos ){
                if( port_list[ list_pos ] == NULL ){
                        found = 1;
                        break;
                }
        }

        if( !found ){
                //no free slots.  Expand our array by 10 elements
                struct port_descriptor** tmpPortDesc;
                tmpPortDesc = malloc( sizeof( struct port_descriptor* ) * ( port_list_size + 10 ) );

                //put all elements into the new array
                for( list_pos = 0; list_pos < port_list_size; ++list_pos ){
                        tmpPortDesc[ list_pos ] = port_list[ list_pos ];
                }
                ++list_pos; //put the place to insert the new record one past the old records

                port_list_size += 10;
       
                //free the old array, set it to the new one
                free( port_list );
                port_list = tmpPortDesc;
        }

        //at this point, list_pos points to a NULL location in our array
        new_port = malloc( sizeof( struct port_descriptor ) );

        //Now, let's get to the actual opening of our port
#ifdef _WIN32
        {
                //GOD DAMN IT WINDOWS http://support.microsoft.com/kb/115831
                char* special_port = malloc( strlen( port_to_open ) + 5 );
                memcpy( special_port, "\\\\.\\", 4 );
                memcpy( special_port + 4, port_to_open, strlen( port_to_open ) + 1 );
                new_port->port = CreateFile( special_port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0 );
                free( special_port );
        }
        if( new_port->port == INVALID_HANDLE_VALUE ){
                if( GetLastError() == ERROR_FILE_NOT_FOUND ){
                        LPTSTR error_text = NULL;
                        jclass exception_class;
                       
                        FormatMessage(
                                FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
                                NULL,
                                GetLastError(),
                                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                (LPTSTR)&error_text,
                                0,
                                NULL );
               
                        (*env)->ExceptionDescribe( env );
                        (*env)->ExceptionClear( env );
                        exception_class = (*env)->FindClass(env, "com/rm5248/serial/NoSuchPortException");
                        (*env)->ThrowNew(env, exception_class, error_text );
                        free( new_port );
                        LocalFree( error_text );
                        return -1;
                }
        }
       
        {
                //Let's check to see if this is a serial port
                DCB io_name = {0};
                io_name.DCBlength = sizeof( io_name );
                if (!GetCommState( new_port->port, &io_name ) ) {
                        LPTSTR error_text = NULL;
                        jclass exception_class;
                        (*env)->ExceptionDescribe( env );
                        (*env)->ExceptionClear( env );
                        exception_class = (*env)->FindClass(env, "com/rm5248/serial/NotASerialPortException");
                        (*env)->ThrowNew(env, exception_class, "You are attempting to open something which is not a serial port" );
                        free( new_port );
                        return -1;
                }
        }
       
#else
        pthread_mutex_init( &(new_port->in_use), NULL );
        new_port->port = open( port_to_open, O_RDWR );
        if( new_port->port < 0 && errno == ENOENT ){
                //That's not a valid serial port, error out
                jclass exception_class;
                (*env)->ExceptionDescribe( env );
                (*env)->ExceptionClear( env );
                exception_class = (*env)->FindClass(env, "com/rm5248/serial/NoSuchPortException");
                (*env)->ThrowNew(env, exception_class, strerror( errno ) );
                free( new_port );
                return -1;
        }else if( new_port->port < 0 ){
                jclass exception_class;
                (*env)->ExceptionDescribe( env );
                (*env)->ExceptionClear( env );
                exception_class = (*env)->FindClass(env, "com/rm5248/serial/NoSuchPortException");
                (*env)->ThrowNew(env, exception_class, strerror( errno ) );
                free( new_port );
                return -1;
        }
       
        {
                struct termios io_name;
                if( tcgetattr( new_port->port, &io_name ) < 0 ){
                        if( errno == ENOTTY ){
                                //This is apparently not a serial port
                                jclass exception_class;
                                (*env)->ExceptionDescribe( env );
                                (*env)->ExceptionClear( env );
                                exception_class = (*env)->FindClass(env, "com/rm5248/serial/NotASerialPortException");
                                (*env)->ThrowNew(env, exception_class, "You are attempting to open something which is not a serial port" );
                                free( new_port );
                                return -1;
                        }
                }
        }
#endif /* __WIN32 */


       
        //Set the baud rate
        if( set_baud_rate( new_port, baudRate ) < 0 ){
                        throw_io_exception_message( env, "Unable to set baud rate" );
                        return 0;
        }
        set_raw_input( new_port );
        //Set the data bits( character size )
        set_data_bits( new_port, dataBits );
        //Set the stop bits
        set_stop_bits( new_port, stopBits );
        //Set the parity
        set_parity( new_port, parity );
        //Set the flow control
        set_flow_control( new_port, flowControl );

        //Only set the new_port to be in our array as the last instruction
        //If there are any errors, we will have returned long before this
        port_list[ list_pos ] = new_port;
       
        return list_pos;
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    openPort
 * Signature: (Ljava/lang/String;)I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_openPort__Ljava_lang_String_2
  (JNIEnv * env, jobject obj, jstring port){
        struct port_descriptor* new_port;
        int list_pos;
        int found = 0;
        const char* port_to_open;
        jboolean iscopy;

        port_to_open = (*env)->GetStringUTFChars( env, port, &iscopy );

        if( port_list == NULL ){
                port_list = malloc( sizeof( struct port_descriptor* ) * 10 );
                port_list_size = 10;
                for( list_pos = 0; list_pos < port_list_size; ++list_pos ){
                        port_list[ list_pos ] = NULL;
                }
        }

        //Search thru the port_list, find the first one that is NULL
        for( list_pos = 0; list_pos < port_list_size; ++list_pos ){
                if( port_list[ list_pos ] == NULL ){
                        found = 1;
                        break;
                }
        }

        if( !found ){
                //no free slots.  Expand our array by 10 elements
                struct port_descriptor** tmpPortDesc;
                tmpPortDesc = malloc( sizeof( struct port_descriptor* ) * ( port_list_size + 10 ) );

                //put all elements into the new array
                for( list_pos = 0; list_pos < port_list_size; ++list_pos ){
                        tmpPortDesc[ list_pos ] = port_list[ list_pos ];
                }
                ++list_pos; //put the place to insert the new record one past the old records

                port_list_size += 10;
       
                //free the old array, set it to the new one
                free( port_list );
                port_list = tmpPortDesc;
        }

        //at this point, list_pos points to a NULL location in our array
        new_port = malloc( sizeof( struct port_descriptor ) );

        //Now, let's get to the actual opening of our port
#ifdef _WIN32
        {
                char* special_port = malloc( strlen( port_to_open ) + 5 );
                memcpy( special_port, "\\\\.\\", 4 );
                memcpy( special_port + 4, port_to_open, strlen( port_to_open ) + 1 );
                new_port->port = CreateFile( special_port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0 );
                free( special_port );
        }
        if( new_port->port == INVALID_HANDLE_VALUE ){
                if( GetLastError() == ERROR_FILE_NOT_FOUND ){
                        LPTSTR error_text = NULL;
                        jclass exception_class;
                       
                        FormatMessage(
                                FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
                                NULL,
                                GetLastError(),
                                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                (LPTSTR)&error_text,
                                0,
                                NULL );
               
                        (*env)->ExceptionDescribe( env );
                        (*env)->ExceptionClear( env );
                        exception_class = (*env)->FindClass(env, "com/rm5248/serial/NoSuchPortException");
                        (*env)->ThrowNew(env, exception_class, error_text );
                        free( new_port );
                        LocalFree( error_text );
                        return -1;
                }
        }
       
        {
                //Let's check to see if this is a serial port
                DCB io_name = {0};
                io_name.DCBlength = sizeof( io_name );
                if (!GetCommState( new_port->port, &io_name ) ) {
                        LPTSTR error_text = NULL;
                        jclass exception_class;
                        (*env)->ExceptionDescribe( env );
                        (*env)->ExceptionClear( env );
                        exception_class = (*env)->FindClass(env, "com/rm5248/serial/NotASerialPortException");
                        (*env)->ThrowNew(env, exception_class, "You are attempting to open something which is not a serial port" );
                        free( new_port );
                        return -1;
                }
        }
#else
        pthread_mutex_init( &(new_port->in_use), NULL );
        new_port->port = open( port_to_open, O_RDWR );
        if( new_port->port < 0 && errno == ENOENT ){
                //That's not a valid serial port, error out
                jclass exception_class;
                (*env)->ExceptionDescribe( env );
                (*env)->ExceptionClear( env );
                exception_class = (*env)->FindClass(env, "com/rm5248/serial/NoSuchPortException");
                (*env)->ThrowNew(env, exception_class, strerror( errno ) );
                free( new_port );
                return -1;
        }else if( new_port->port < 0 ){
                jclass exception_class;
                (*env)->ExceptionDescribe( env );
                (*env)->ExceptionClear( env );
                exception_class = (*env)->FindClass(env, "com/rm5248/serial/NoSuchPortException");
                (*env)->ThrowNew(env, exception_class, strerror( errno ) );
                free( new_port );
                return -1;
        }
       
        {
                struct termios io_name;
                if( tcgetattr( new_port->port, &io_name ) < 0 ){
                        if( errno == ENOTTY ){
                                //This is apparently not a serial port
                                jclass exception_class;
                                (*env)->ExceptionDescribe( env );
                                (*env)->ExceptionClear( env );
                                exception_class = (*env)->FindClass(env, "com/rm5248/serial/NotASerialPortException");
                                (*env)->ThrowNew(env, exception_class, "You are attempting to open something which is not a serial port" );
                                free( new_port );
                                return -1;
                        }
                }
        }
#endif /* __WIN32 */

       
        //Only set the new_port to be in our array as the last instruction
        //If there are any errors, we will have returned long before this
        port_list[ list_pos ] = new_port;

        return list_pos;
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    doClose
 * Signature: ()V
 */

JNIEXPORT void JNICALL Java_com_rm5248_serial_SerialPort_doClose
  (JNIEnv * env, jobject obj){
        //Note: We can't use get_serial_port_struct here, beacuse we need to set
        //the position in the array to NULL
        int array_pos;
        struct port_descriptor* desc;
       
        array_pos = get_handle( env, obj );
        if( array_pos < 0 || array_pos > port_list_size ){
                throw_io_exception_message( env, "Unable to get handle" );
                return;
        }
        desc = port_list[ array_pos ];
        if( desc == NULL ){
                throw_io_exception_message( env, "Unable to get descriptor" );
                return;
        }
       
        close( desc->port );
#ifndef _WIN32
        pthread_mutex_lock( &(desc->in_use) );
        pthread_mutex_unlock( &(desc->in_use) );
#endif
        free( port_list[ array_pos ] );
        port_list[ array_pos ] = NULL;
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    setBaudRate
 * Signature: (I)Z
 */

JNIEXPORT jboolean JNICALL Java_com_rm5248_serial_SerialPort_setBaudRate
  (JNIEnv * env, jobject obj, jint baud_rate ){
        struct port_descriptor* desc;

        desc = get_port_descriptor( env, obj );

        if( desc == NULL ){
                return 0;
        }

        return set_baud_rate( desc, baud_rate );
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    getBaudRateInternal
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_getBaudRateInternal
  (JNIEnv * env, jobject obj){
        struct port_descriptor* desc;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }

        //Now, let's get the baud rate information
        {
                GET_SERIAL_PORT_STRUCT( desc->port, newio );
#ifdef _WIN32
                GetCommState( desc->port, &newio );
                switch( newio.BaudRate ){
#else
                switch( cfgetispeed( &newio ) ){
                GET_SPEED_SWITCH( 0, newio );
                GET_SPEED_SWITCH( 50, newio );
                GET_SPEED_SWITCH( 75, newio );
#endif /* _WIN32 */
                GET_SPEED_SWITCH( 110, newio );
#ifndef _WIN32
                GET_SPEED_SWITCH( 134, newio );
                GET_SPEED_SWITCH( 150, newio );
                GET_SPEED_SWITCH( 200, newio );
#endif /* _WIN32 */
                GET_SPEED_SWITCH( 300, newio );
                GET_SPEED_SWITCH( 600, newio );
                GET_SPEED_SWITCH( 1200, newio );
#ifndef _WIN32
                GET_SPEED_SWITCH( 1800, newio );
#endif /* _WIN32 */
                GET_SPEED_SWITCH( 2400, newio );
                GET_SPEED_SWITCH( 4800, newio );
                GET_SPEED_SWITCH( 9600, newio );
                GET_SPEED_SWITCH( 19200, newio );
                GET_SPEED_SWITCH( 38400, newio );
                GET_SPEED_SWITCH( 115200, newio );
                default:
                        return 0;
                } /* end switch */
        }

}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    setStopBits
 * Signature: (I)Z
 */

JNIEXPORT jboolean JNICALL Java_com_rm5248_serial_SerialPort_setStopBits
  (JNIEnv * env, jobject obj, jint bits){
        struct port_descriptor* desc;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }
       
        return set_stop_bits( desc, bits );
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    getStopBitsInternal
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_getStopBitsInternal
  (JNIEnv * env, jobject obj){
        struct port_descriptor* desc;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }

        {
                GET_SERIAL_PORT_STRUCT( desc->port, newio );
#ifdef _WIN32
                if( newio.StopBits == 1 ){
                        return 1;
                }else if( newio.StopBits == 2 ){
                        return 2;
                }else{
                        return -1;
                }
#else
                if( newio.c_cflag & CSTOPB ){
                        return 2;
                }else{
                        return 1;
                }
#endif
        }
}
/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    setCharSize
 * Signature: (I)Z
 */

JNIEXPORT jboolean JNICALL Java_com_rm5248_serial_SerialPort_setCharSize
  (JNIEnv * env, jobject obj, jint size){
        struct port_descriptor* desc;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }
       
        return set_data_bits( desc, size );
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    getCharSizeInternal
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_getCharSizeInternal
  (JNIEnv * env, jobject obj){
        struct port_descriptor* desc;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }

        //Now get the char size
        {
                GET_SERIAL_PORT_STRUCT( desc->port, newio );
               
#ifdef _WIN32
                return newio.ByteSize;
#else
                if( ( newio.c_cflag | CS8 ) == CS8 ){
                        return 8;
                }else if( ( newio.c_cflag | CS7 ) == CS7 ){
                        return 7;
                }else if( ( newio.c_cflag | CS6 ) == CS6 ){
                        return 6;
                }else if( ( newio.c_cflag | CS5 ) == CS5 ){
                        return 5;
                }else{
                        return 0;
                }
#endif
        }

}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    setParity
 * Signature: (I)Z
 */

JNIEXPORT jboolean JNICALL Java_com_rm5248_serial_SerialPort_setParity
  (JNIEnv * env, jobject obj, jint parity){
        struct port_descriptor* desc;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }      
       
        return set_parity( desc, parity );
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    getParityInternal
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_getParityInternal
  (JNIEnv * env, jobject obj){
        struct port_descriptor* desc;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }

        {
                GET_SERIAL_PORT_STRUCT( desc->port, newio );
#ifdef _WIN32
                if( newio.Parity == NOPARITY ){
                        return 0;
                }else if( newio.Parity == ODDPARITY ){
                        return 1;
                }else if( newio.Parity == EVENPARITY ){
                        return 2;
                }else{
                        return -1;
                }
#else
                if( !( newio.c_cflag & PARENB ) ){
                        //No parity
                        return 0;
                }else if( newio.c_cflag & PARODD ){
                        //Odd parity
                        return 1;
                }else if( !( newio.c_cflag & PARODD ) ){
                        //Even parity
                        return 2;
                }else{
                        return -1;
                }
#endif
        }

}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    setFlowControl
 * Signature: (I)Z
 */

JNIEXPORT jboolean JNICALL Java_com_rm5248_serial_SerialPort_setFlowControl
  (JNIEnv * env, jobject obj, jint flow){
        struct port_descriptor* desc;
       
        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }
       
        return set_flow_control( desc, flow );
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    getFlowControlInternal
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_getFlowControlInternal
  (JNIEnv * env, jobject obj){
        struct port_descriptor* desc;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }
       
        {
                GET_SERIAL_PORT_STRUCT( desc->port, newio );
#ifdef _WIN32
                if( newio.fOutX == TRUE && newio.fInX == TRUE ){
                        return 2;
                }else if( newio.fRtsControl == TRUE && newio.fOutxCtsFlow == TRUE ){
                        return 1;
                }else{
                        return 0;
                }
#else
                if( newio.c_cflag & ~( IXON ) &&
                        newio.c_cflag & ~( IXOFF ) &&
                        newio.c_cflag & ~( IXANY ) ){
                        return 0;
                }else if( newio.c_cflag & HW_FLOW ){
                        return 1;
                }else if( newio.c_cflag & ( IXON ) &&
                        newio.c_cflag & ( IXOFF ) &&
                        newio.c_cflag & ( IXANY ) ){
                        return 2;
                }
#endif /* _WIN32 */
        }

        return -1;
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    getSerialLineStateInternalNonblocking
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_getSerialLineStateInternalNonblocking
  (JNIEnv * env, jobject obj ){
        struct port_descriptor* desc;
        jint ret_val;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }

        ret_val = 0;
       
        {      
#ifdef _WIN32
                DWORD get_val;
                if( GetCommModemStatus( desc->port, &get_val ) == 0 ){
                        throw_io_exception( env, GetLastError() );
                        return -1;
                }
               
                if( get_val & MS_CTS_ON ){
                        // CTS
                        ret_val |= ( 0x01 << 1 );
                }
               
                if( get_val & MS_DSR_ON ){
                        // Data Set Ready
                        ret_val |= ( 0x01 << 2 );
                }
               
                if( winDTR ){
                        ret_val |= ( 0x01 << 3 );
                }
               
                if( winRTS ){
                        ret_val |= ( 0x01 << 4 );
                }
               
                if( get_val & MS_RING_ON ){
                        // Ring Indicator
                        ret_val |= ( 0x01 << 5 );
                }
#else
                int get_val;
                if( ioctl( desc->port, TIOCMGET, &get_val ) < 0 ){
                        throw_io_exception( env, errno );
                        return -1;
                }

                if( get_val & TIOCM_CD ){
                        // Carrier detect
                        ret_val |= 0x01;
                }

                if( get_val & TIOCM_CTS ){
                        // CTS
                        ret_val |= ( 0x01 << 1 );
                }

                if( get_val & TIOCM_DSR ){
                        // Data Set Ready
                        ret_val |= ( 0x01 << 2 );
                }

                if( get_val & TIOCM_DTR ){
                        // Data Terminal Ready
                        ret_val |= ( 0x01 << 3 );
                }

                if( get_val & TIOCM_RTS ){
                        // Request To Send
                        ret_val |= ( 0x01 << 4 );
                }

                if( get_val & TIOCM_RI ){
                        // Ring Indicator
                        ret_val |= ( 0x01 << 5 );
                }
#endif
        }

        return ret_val;
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    setSerialLineStateInternal
 * Signature: (Lcom/rm5248/serial/SerialLineState;)I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_setSerialLineStateInternal
  (JNIEnv * env, jobject obj, jobject serial){
        struct port_descriptor* desc;
        jint ret_val;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }

        ret_val = 0;
       
#ifdef _WIN32
        if( get_bool( env, serial, "dataTerminalReady" ) ){
                if( !EscapeCommFunction( desc->port, SETDTR ) ){
                        throw_io_exception_message( env, "Could not set DTR" );
                        return -1;
                }
                winDTR = 1;
        }else{
                if( !EscapeCommFunction( desc->port, CLRDTR ) ){
                        throw_io_exception_message( env, "Could not set DTR" );
                        return -1;
                }
                winDTR = 0;
        }

        if( get_bool( env, serial, "requestToSend" ) ){
                if( !EscapeCommFunction( desc->port, SETRTS ) ){
                        throw_io_exception_message( env, "Could not set RTS" );
                        return -1;
                }
                winRTS = 1;
        }else{
                if( !EscapeCommFunction( desc->port, CLRRTS ) ){
                        throw_io_exception_message( env, "Could not set RTS" );
                        return -1;
                }
                winRTS = 0;
        }
#else
        int toSet = 0;

        if( ioctl( desc->port, TIOCMGET, &toSet ) < 0 ){
                throw_io_exception_message( env, "Could not get port settings" );
                return -1;
        }

        if( get_bool( env, serial, "dataTerminalReady" ) ){
                toSet |= TIOCM_DTR;
        }else{
                toSet &= ~TIOCM_DTR;
        }

        if( get_bool( env, serial, "requestToSend" ) ){
                toSet |= TIOCM_RTS;
        }else{
                toSet &= ~TIOCM_RTS;
        }

        if( ioctl( desc->port, TIOCMSET, &toSet ) < 0 ){
                throw_io_exception_message( env, "Could not set port settings" );
        }
#endif

        return ret_val;
}

//
// ------------------------------------------------------------------------
// ------------------Input/Output methods below here-----------------------
// ------------------------------------------------------------------------
//


/*
 * Class:     com_rm5248_serial_SerialInputStream
 * Method:    readByte
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialInputStream_readByte
  (JNIEnv * env, jobject obj){
        int stat;
        int ret_val;
        struct port_descriptor* desc;
        int get_val = 0;
#ifdef _WIN32
        DWORD ret = 0;
        OVERLAPPED overlap = {0};
        int current_available = 0;
#endif

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }
       
        ret_val = 0;

#ifdef _WIN32
        {
                DWORD comErrors = {0};
                COMSTAT portStatus = {0};
                if( !ClearCommError( desc->port, &comErrors, &portStatus ) ){
                        //return value zero = fail
                        throw_io_exception( env, GetLastError() );
                        return -1;
                }else{
                        current_available = portStatus.cbInQue;
                }
        }
       
        if( !current_available ){
                //If nothing is currently available, wait until we get an event of some kind.
                //This could be the serial lines changing state, or it could be some data
                //coming into the system.
                overlap.hEvent = CreateEvent( 0, TRUE, 0, 0 );
                SetCommMask( desc->port, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING );
                WaitCommEvent( desc->port, &ret, &overlap );
                WaitForSingleObject( overlap.hEvent, INFINITE );
        }else{
                //Data is available; set the RXCHAR mask so we try to read from the port
                ret = EV_RXCHAR;
        }
               
        if( ret & EV_RXCHAR ){
                if( !ReadFile( desc->port, &ret_val, 1, &stat, &overlap) ){
                        throw_io_exception( env, GetLastError() );
                        return -1;
                }
               
                //This is a valid byte, set our valid bit
                ret_val |= ( 0x01 << 15 );
        }
       
        //Always get the com lines no matter what      
        if( GetCommModemStatus( desc->port, &get_val ) == 0 ){
                throw_io_exception( env, GetLastError() );
                return -1;
        }
               
        if( get_val & MS_CTS_ON ){
                // CTS
                ret_val |= ( 0x01 << 10 );
        }
               
        if( get_val & MS_DSR_ON ){
                // Data Set Ready
                ret_val |= ( 0x01 << 11 );
        }
       
        if( winDTR ){
                ret_val |= ( 0x01 << 12 );
        }
       
        if( winRTS ){
                ret_val |= ( 0x01 << 13 );
        }
               
        if( get_val & MS_RING_ON ){
                // Ring Indicator
                ret_val |= ( 0x01 << 14 );
        }
       
#else

        //Okay, this here is going to be a bit ugly.
        //The problem here is that Linux/POSIX don't specify that TIOCMIWAIT
        //has to exist.  Also, the fact that if we use TIOCMIWAIT we don't
        //timeout or anything.  What would be very convenient in this case
        //would be to have select() return when the state changed, but
        //it's not possible. :(
        //Ironically, this is one case in which the Windows serial API
        //is better than the POSIX way.
        fd_set fdset;
        struct timeval timeout;
        int originalState;
        int selectStatus;

        pthread_mutex_lock( &(desc->in_use) );

        //first get the original state of the serial port lines
        if( ioctl( desc->port, TIOCMGET, &originalState ) < 0 ){
                throw_io_exception( env, errno );
                pthread_mutex_unlock( &(desc->in_use) );
                return -1;
        }
       
        while( 1 ){
                FD_ZERO( &fdset );
                FD_SET( desc->port, &fdset );
                timeout.tv_sec = 0;
                timeout.tv_usec = 10000; // 10,000 microseconds = 10ms

                selectStatus = select( desc->port + 1, &fdset, NULL, NULL, &timeout );
                if( selectStatus < 0 ){
                        int errval;
                        if( errno == EBADF ){
                                // EOF
                                errval= 0;
                        }else{
                                throw_io_exception( env, errno );
                                errval = -1;
                        }
                        pthread_mutex_unlock( &(desc->in_use) );
                        return errval;
                }

                if( selectStatus == 0 ){
                        //This was a timeout
                        if( ioctl( desc->port, TIOCMGET, &get_val ) < 0 ){
                                throw_io_exception( env, errno );
                                pthread_mutex_unlock( &(desc->in_use) );
                                return -1;
                        }

                        if( get_val == originalState ){
                                //The state of the lines have not changed,
                                //continue on until something changes
                                continue;
                        }
                       
                }
       
                if( FD_ISSET( desc->port, &fdset ) ){
                        stat = read( desc->port, &ret_val, 1 );
                        if( stat < 0 ){
                                //throw new exception
                                throw_io_exception( env, errno );
                                pthread_mutex_unlock( &(desc->in_use) );
                                return -1;
                        }
               
                        //This is a valid byte, set our valid bit
                        ret_val |= ( 0x01 << 15 );
                }
               
                //We get to this point if we either 1. have data or
                //2. our state has changed
                break;
        }

        //Now that we have read in the character, let's get the serial port line state.
        //If it has changed, we will fire an event in Java.
        //Now, because we only read one byte at a time, we will use the lower 8 bytes to
        //return the character that we read.  The other bytes will be used to return
        //information on our serial port state.
        if( ioctl( desc->port, TIOCMGET, &get_val ) < 0 ){
                throw_io_exception( env, errno );
                pthread_mutex_unlock( &(desc->in_use) );
                return -1;
        }

        if( get_val & TIOCM_CD ){
                // Carrier detect
                ret_val |= ( 0x01 << 9 );
        }

        if( get_val & TIOCM_CTS ){
                // CTS
                ret_val |= ( 0x01 << 10 );
        }

        if( get_val & TIOCM_DSR ){
                // Data Set Ready
                ret_val |= ( 0x01 << 11 );
        }

        if( get_val & TIOCM_DTR ){
                // Data Terminal Ready
                ret_val |= ( 0x01 << 12 );
        }
               
        if( get_val & TIOCM_RTS ){
                // Request To Send
                ret_val |= ( 0x01 << 13 );
        }
       
        if( get_val & TIOCM_RI ){
                // Ring Indicator
                ret_val |= ( 0x01 << 14 );
        }
       
        pthread_mutex_unlock( &(desc->in_use) );
#endif

        return ret_val;
}

/*
 * Class:     com_rm5248_serial_SerialInputStream
 * Method:    getAvailable
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialInputStream_getAvailable
  (JNIEnv * env, jobject obj){
        jint ret_val;
        struct port_descriptor* desc;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }

#ifdef _WIN32
        {
                DWORD comErrors = {0};
                COMSTAT portStatus = {0};
                if( !ClearCommError( desc->port, &comErrors, &portStatus ) ){
                        //return value zero = fail
                        throw_io_exception( env, GetLastError() );
                        return -1;
                }else{
                        ret_val = portStatus.cbInQue;
                }
        }
#else
        if( ioctl( desc->port, FIONREAD, &ret_val ) < 0 ){
                //throw new exception
                throw_io_exception( env, errno );
                return -1;
        }
#endif
        return ret_val;
}

/*
 * Class:     com_rm5248_serial_SimpleSerialInputStream
 * Method:    readByte
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SimpleSerialInputStream_readByte
  (JNIEnv * env, jobject obj){
        struct port_descriptor* desc;
        jint ret_val;
        int stat;
#ifdef _WIN32
        DWORD ret = 0;
        OVERLAPPED overlap = {0};
        int current_available = 0;
#endif

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return 0;
        }
       
        ret_val = 0;

#ifdef _WIN32
        {
                DWORD comErrors = {0};
                COMSTAT portStatus = {0};
                if( !ClearCommError( desc->port, &comErrors, &portStatus ) ){
                        //return value zero = fail
                        throw_io_exception( env, GetLastError() );
                        return -1;
                }else{
                        current_available = portStatus.cbInQue;
                }
        }
       
        if( !current_available ){
                //If nothing is currently available, wait until we get an event of some kind.
                //This could be the serial lines changing state, or it could be some data
                //coming into the system.
                overlap.hEvent = CreateEvent( 0, TRUE, 0, 0 );
                SetCommMask( desc->port, EV_RXCHAR );
                WaitCommEvent( desc->port, &ret, &overlap );
                WaitForSingleObject( overlap.hEvent, INFINITE );
        }else{
                //Data is available; set the RXCHAR mask so we try to read from the port
                ret = EV_RXCHAR;
        }
               
        if( ret & EV_RXCHAR ){
                if( !ReadFile( desc->port, &ret_val, 1, &stat, &overlap) ){
                        throw_io_exception( env, GetLastError() );
                        return -1;
                }
        }
#else
        pthread_mutex_lock( &(desc->in_use) );
        stat = read( desc->port, &ret_val, 1 );
        if( stat < 0 ){
                throw_io_exception( env, errno );
                pthread_mutex_unlock( &(desc->in_use) );
                return -1;
        }
        pthread_mutex_unlock( &(desc->in_use) );
#endif
        return ret_val;
}

/*
 * Class:     com_rm5248_serial_SimpleSerialInputStream
 * Method:    getAvailable
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SimpleSerialInputStream_getAvailable
  (JNIEnv * env, jobject obj){
        //use our already-existing method to get the available bytes, it already works
        return Java_com_rm5248_serial_SerialInputStream_getAvailable( env, obj );
}


/*
 * Class:     com_rm5248_serial_SerialOutputStream
 * Method:    writeByte
 * Signature: (I)V
 */

JNIEXPORT void JNICALL Java_com_rm5248_serial_SerialOutputStream_writeByte
  (JNIEnv * env, jobject obj, jint byte){
        struct port_descriptor* desc;
        char byte_write;
#ifdef _WIN32
        DWORD bytes_written;
        OVERLAPPED overlap;
#else
        int bytes_written;
#endif

        byte_write = byte;

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return;
        }

#ifdef _WIN32
        memset( &overlap, 0, sizeof( overlap ) );
        overlap.hEvent = CreateEvent( 0, TRUE, 0, 0 );
        if( !WriteFile( desc->port, &byte_write, sizeof( byte_write ), &bytes_written, &overlap ) ){
                if( GetLastError() == ERROR_IO_PENDING ){
                        //Probably not an error, we're just doing this in an async fasion
                        if( WaitForSingleObject( overlap.hEvent, INFINITE ) == WAIT_FAILED ){
                                throw_io_exception( env, GetLastError() );
                                return;
                        }
                }else{
                        throw_io_exception( env, GetLastError() );
                        return;
                }
        }
#else
        bytes_written = write( desc->port, &byte_write, sizeof( byte_write ) );
        if( bytes_written < 0 ){
                //throw new exception
                throw_io_exception( env, errno );
                return;
        }
#endif
}

/*
 * Class:     com_rm5248_serial_SerialOutputStream
 * Method:    writeByteArray
 * Signature: ([B)V
 */

JNIEXPORT void JNICALL Java_com_rm5248_serial_SerialOutputStream_writeByteArray
  (JNIEnv * env, jobject obj, jbyteArray arr){
        jbyte* data;
        jint len;
        struct port_descriptor* desc;
#ifdef _WIN32
        DWORD bytes_written;
        OVERLAPPED overlap;
#else
        int bytes_written;
#endif /* _WIN32 */

        desc = get_port_descriptor( env, obj );
        if( desc == NULL ){
                return;
        }

        len = (*env)->GetArrayLength( env, arr );
        data = (*env)->GetByteArrayElements(env, arr, 0);

#ifdef _WIN32
        memset( &overlap, 0, sizeof( overlap ) );
        overlap.hEvent = CreateEvent( 0, TRUE, 0, 0 );
        if( !WriteFile( desc->port, data, len, &bytes_written, &overlap ) ){
                if( GetLastError() == ERROR_IO_PENDING ){
                        //Probably not an error, we're just doing this in an async fasion
                        if( WaitForSingleObject( overlap.hEvent, INFINITE ) == WAIT_FAILED ){
                                throw_io_exception( env, GetLastError() );
                                return;
                        }
                }else{
                        throw_io_exception( env, GetLastError() );
                }
        }
       
#else
        bytes_written = write( desc->port, data, len );
        if( bytes_written < 0 ){
                throw_io_exception( env, errno );
        }
#endif /* _WIN32 */

        (*env)->ReleaseByteArrayElements(env, arr, data, 0);
}


//
// ------------------------------------------------------------------------
// ---------------------Static methods below here--------------------------
// ------------------------------------------------------------------------
//

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    getMajorNativeVersion
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_getMajorNativeVersion
  (JNIEnv * env, jclass cls){
        return 0;
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    getMinorNativeVersion
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_rm5248_serial_SerialPort_getMinorNativeVersion
  (JNIEnv * env, jclass cls){
        return 5;
}

/*
 * Class:     com_rm5248_serial_SerialPort
 * Method:    getSerialPorts
 * Signature: ()[Ljava/lang/String;
 */

JNIEXPORT jobjectArray JNICALL Java_com_rm5248_serial_SerialPort_getSerialPorts
  (JNIEnv * env, jclass cls){
        jclass stringClass;
    jobjectArray array;
        char** port_names;
        int port_names_size;
        int x;
       
        port_names_size = 0;
        port_names = malloc( sizeof( char* ) * 255 ); //max 255 serial ports
#ifdef _WIN32
        {
                //Brute force, baby!
                char* port_to_open = malloc( 11 );
                HANDLE* port;
                for( x = 0; x <= 255; x++ ){
                        _snprintf_s( port_to_open, 11, 11, "\\\\.\\COM%d", x );
                        port = CreateFile( port_to_open, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 );
                        if( port != INVALID_HANDLE_VALUE ||
                                ( port == INVALID_HANDLE_VALUE &&
                                GetLastError() != ERROR_FILE_NOT_FOUND ) ){
                                //This is a valid port
                                //we could get INVALID_HANDLE_VALUE if the port is already open,
                                //so make sure we check to see if it is not that value
                                port_names[ port_names_size ] = malloc( 6 );
                                memcpy( port_names[ port_names_size ], port_to_open + 4, 6 );
                                port_names_size++;
                        }
                        CloseHandle( port );
                }
                free( port_to_open );
        }
#else
        {
                struct dirent *entry;
                DIR* dir;
                int fd;
                char* deviceName;

                deviceName = malloc( 100 );

                dir = opendir( "/dev/" );
                if( dir == NULL ){
                        throw_io_exception_message( env, "We can't open /dev." );
                        free( port_names );
                        free( deviceName );
                        return NULL;
                }
                while ( entry = readdir( dir ), entry != NULL) {
                        if( snprintf( deviceName, 100, "/dev/%s", entry->d_name ) >= 99 ){
                                fprintf( stderr, "WARNING: Ignoring file %s, filename too long\n", entry->d_name );
                                continue;
                        }
                        fd = open( deviceName, O_RDONLY );
                        if( fd < 0 ){
                                switch( errno ){
                                        case EACCES:
                                        case ENOMEDIUM:
                                                //For some reason, I get errno 22 on my laptop
                                                //when opening /dev/video0, which prints out
                                                //"No such device or address"
                                                //Not adding it here, because that does seem bad
                                                break;
                                        default:
                                                perror( "open" );
                                                fprintf( stderr, "ERROR: This is a very bad thing that should never happen %s:%d  errno:%d\n", __FILE__, __LINE__, errno );
                                }
                                close( fd );
                                continue;
                        }
                       
                        if( isatty( fd ) && port_names_size < 255 ){
                                port_names[ port_names_size ] = malloc( strlen( entry->d_name ) + 6 );
                                memcpy( port_names[ port_names_size ], "/dev/", 5 );
                                memcpy( port_names[ port_names_size ] + 5, entry->d_name, strlen( entry->d_name ) + 1 );
                                port_names_size++;
                        }
                       
                        close( fd );
                }
                closedir( dir );
                free( deviceName );
        }
#endif /* _WIN32 */

        stringClass = (*env)->FindClass(env, "java/lang/String");
        array = (*env)->NewObjectArray(env, port_names_size, stringClass, 0);
       
        for( x = 0; x < port_names_size; x++ ){
                (*env)->SetObjectArrayElement(env, array, x, (*env)->NewStringUTF( env, port_names[ x ] ) );
                free( port_names[ x ] );
        }
       
        free( port_names );
       
        return array;
}