/*-------------------------------------------------------------------------
|   rxtx is a native interface to serial ports in java.
|   Copyright 2002-2004 Michal Hobot MichalHobot@netscape.net
|   Copyright 1997-2004 by Trent Jarvi taj@parcelfarce.linux.theplanet.co.uk
|
|   This library is free software; you can redistribute it and/or
|   modify it under the terms of the GNU Library General Public
|   License as published by the Free Software Foundation; either
|   version 2 of the License, or (at your option) any later version.
|
|   This library is distributed in the hope that it will be useful,
|   but WITHOUT ANY WARRANTY; without even the implied warranty of
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
|   Library General Public License for more details.
|
|   You should have received a copy of the GNU Library General Public
|   License along with this library; if not, write to the Free
|   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
--------------------------------------------------------------------------*/
#include "StdAfx.h"
#include "rxtxHelpers.h"

/*
Initialize

   accept:      none
   perform:     Initialize the native library
   return:      none
   exceptions:  none
   comments:
 * Class:     gnu_io_RXTXPort
 * Method:    Initialize
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_Initialize(JNIEnv *env, jclass cls)
{
}

/*
open

   accept:      The device to open. ie "COM1:"
   perform:     open the device
   return:      handle
   exceptions:  IOExcepiton
   comments:    Very often people complain about not being able to get past
                this function and it turns out to be permissions on the
                device file or bios has the device disabled.
 * Class:     gnu_io_RXTXPort
 * Method:    open
 * Signature: (Ljava/lang/String{ })I
 */
JNIEXPORT jint JNICALL Java_gnu_io_RXTXPort_open(JNIEnv *env, jobject jobj, jstring name)
{
  DCB PortDCB;
  COMMTIMEOUTS CommTimeouts;
  LPCWSTR lpMsgBuf;
  EventInfoStruct *EventInfo;
  DWORD dwErr;

  LPCWSTR wszName = env->GetStringChars(name, NULL);
  HANDLE hPort = CreateFileW(wszName,      // Pointer to the name of the port
                             GENERIC_READ | GENERIC_WRITE,// Access (read-write) mode
                             0,            // Share mode
                             NULL,         // Pointer to the security attribute
                             OPEN_EXISTING,// How to open the serial port
                             0,            // Port attributes
                             NULL);        // Handle to port with attribute to copy
  // If it fails to open the port, return FALSE.
  if ( hPort == INVALID_HANDLE_VALUE )
  { // Could not open the port.
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, PORT_IN_USE_EXCEPTION, L"open - CreateFile", lpMsgBuf );
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    env->ReleaseStringChars(name, wszName);
    return (jint)INVALID_HANDLE_VALUE;
  }


  PortDCB.DCBlength = sizeof (DCB);

  // Get the default port setting information.
  GetCommState(hPort, &PortDCB);

  // Change the DCB structure settings.
  PortDCB.fBinary = TRUE;               // Binary mode; no EOF check
  PortDCB.fParity = TRUE;               // Enable parity checking
  PortDCB.fOutxDsrFlow = FALSE;         // No DSR output flow control
  PortDCB.fDtrControl = DTR_CONTROL_HANDSHAKE;// DTR flow control type
  PortDCB.fDsrSensitivity = FALSE;      // DSR sensitivity
  PortDCB.fTXContinueOnXoff = TRUE;     // XOFF continues Tx
  PortDCB.fErrorChar = FALSE;           // Disable error replacement
  PortDCB.fNull = FALSE;                // Disable null stripping
  PortDCB.fRtsControl = RTS_CONTROL_ENABLE;// RTS flow control
  PortDCB.fAbortOnError = FALSE;        // Do not abort reads/writes on error

  // Configure the port according to the specifications of the DCB structure.
  if (!SetCommState(hPort, &PortDCB))
  { // Could not set comm port state
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, PORT_IN_USE_EXCEPTION, L"open - SetCommState", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    env->ReleaseStringChars(name, wszName);
    return (jint)INVALID_HANDLE_VALUE;
  }

  // Retrieve the time-out parameters for all read and write operations
  // on the port.
  GetCommTimeouts(hPort, &CommTimeouts);

  // Change the COMMTIMEOUTS structure settings.
  CommTimeouts.ReadIntervalTimeout = 0;
  CommTimeouts.ReadTotalTimeoutMultiplier = 0;
  CommTimeouts.ReadTotalTimeoutConstant = MAXDWORD;
  CommTimeouts.WriteTotalTimeoutMultiplier = 10;
  CommTimeouts.WriteTotalTimeoutConstant = 1000;

  // Set the time-out parameters for all read and write operations
  // on the port.
  if (!SetCommTimeouts(hPort, &CommTimeouts))
  { // Unable to set the time-out parameters
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, PORT_IN_USE_EXCEPTION, L"open - SetCommTimeouts", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    env->ReleaseStringChars(name, wszName);
    return (jint)INVALID_HANDLE_VALUE;
  }

  // SETRTS: Sends the RTS (request-to-send) signal.
  EscapeCommFunction(hPort, SETRTS);


  if(dwErr = InitialiseEventInfoStruct(hPort, &EventInfo))
  { // Unable to set up EventInfo structure for event processing
    CreateErrorMsg(dwErr, lpMsgBuf);
    throw_java_exceptionW(env, IO_EXCEPTION, L"open - InitialiseEventInfoStruct", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    env->ReleaseStringChars(name, wszName);
    return (jint)INVALID_HANDLE_VALUE;
  }

  jclass cls = env->GetObjectClass(jobj);
  jfieldID jfEis = env->GetFieldID(cls, "eis", "I");
	if( !jfEis ) {
    IF_DEBUG
    (
		  env->ExceptionDescribe();
    )
		env->ExceptionClear();
    // Free the buffers.
    env->ReleaseStringChars(name, wszName);
		return (jint)INVALID_HANDLE_VALUE;
	}
	env->SetIntField(jobj, jfEis, (jint)EventInfo);

  env->ReleaseStringChars(name, wszName);
  // Returning HANDLE (which is a pointer) as file descriptor... Anyway, 32 bits are 32 bits
  return (jint)hPort;
}


/*
 nativeSetSerialPortParams

   accept:     speed, data bits, stop bits, parity
   perform:    set the serial port parameters
   return:     void
   exceptions: UnsupportedCommOperationException
 * Class:     gnu_io_RXTXPort
 * Method:    nativeSetSerialPortParams
 * Signature: (IIII)V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_nativeSetSerialPortParams(JNIEnv *env, jobject jobj, jint speed, jint dataBits, jint stopBits, jint parity)
{
  DCB PortDCB;
  LPCWSTR lpMsgBuf;

  HANDLE hPort = get_fd(env, jobj);
  PortDCB.DCBlength = sizeof(DCB);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.nativeSetSerialPortParams(%ld, %ld, %ld, %ld) called\n", speed, dataBits, stopBits, parity);
  )

  // Get the default port setting information.
  if(!GetCommState(hPort, &PortDCB))
  { //Unable to get configuration of the serial port
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeSetSerialPortParams - GetCommState", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    return;
  }

  // Change the DCB structure settings.
  PortDCB.BaudRate = speed;             // Current baud
  PortDCB.ByteSize = (BYTE)dataBits;    // Number of bits/byte, 4-8
  PortDCB.Parity = (BYTE)parity;        // 0-4=no,odd,even,mark,space
  switch(stopBits)
  {
    case gnu_io_RXTXPort_STOPBITS_1:
      PortDCB.StopBits = ONESTOPBIT;
      break;

    case gnu_io_RXTXPort_STOPBITS_2:
      PortDCB.StopBits = TWOSTOPBITS;
      break;

    case gnu_io_RXTXPort_STOPBITS_1_5:
      PortDCB.StopBits = ONE5STOPBITS;
      break;

    default:
      throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeSetSerialPortParams", L"Incorrect stopBits");

  }

  // Configure the port according to the specifications of the DCB structure.
  if (!SetCommState (hPort, &PortDCB))
  { //Unable to configure the serial port
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeSetSerialPortParams - SetCommState", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    return;
  }

}

/*
setflowcontrol

   accept:      flowmode
	FLOWCONTROL_NONE        none
	FLOWCONTROL_RTSCTS_IN   hardware flow control
	FLOWCONTROL_RTSCTS_OUT         ""
	FLOWCONTROL_XONXOFF_IN  input software flow control
	FLOWCONTROL_XONXOFF_OUT output software flow control
   perform:     set flow control to flowmode
   return:      none
   exceptions:  UnsupportedCommOperationException
   comments:  there is no differentiation between input and output hardware
              flow control
 * Class:     gnu_io_RXTXPort
 * Method:    setflowcontrol
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_setflowcontrol(JNIEnv *env, jobject jobj, jint flowcontrol)
{
  DCB PortDCB;
  LPCWSTR lpMsgBuf;

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.setflowcontrol(%ld) called\n", flowcontrol);
  )

  HANDLE hPort = get_fd(env, jobj);

  PortDCB.DCBlength = sizeof(DCB);
  if(!GetCommState (hPort, &PortDCB))
  { //Unable to get configuration of the serial port
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"setflowcontrol - GetCommState", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    return;
  }

  if(flowcontrol & (gnu_io_RXTXPort_FLOWCONTROL_RTSCTS_IN | gnu_io_RXTXPort_FLOWCONTROL_RTSCTS_OUT))
    PortDCB.fRtsControl = RTS_CONTROL_HANDSHAKE;
  else
    PortDCB.fRtsControl = RTS_CONTROL_ENABLE;

  if(flowcontrol & gnu_io_RXTXPort_FLOWCONTROL_RTSCTS_OUT)
    PortDCB.fOutxCtsFlow = TRUE;
  else
    PortDCB.fOutxCtsFlow = FALSE;

  if(flowcontrol & gnu_io_RXTXPort_FLOWCONTROL_XONXOFF_IN)
    PortDCB.fInX = TRUE;
  else
    PortDCB.fInX = FALSE;

  if(flowcontrol & gnu_io_RXTXPort_FLOWCONTROL_XONXOFF_OUT)
    PortDCB.fOutX = TRUE;
  else
    PortDCB.fOutX = FALSE;

  // Configure the port according to the specifications of the DCB structure.
  if (!SetCommState (hPort, &PortDCB))
  { // Could not set params
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"setflowcontrol - SetCommState", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    return;
  }
}

/*
NativegetReceiveTimeout

   accept:     none
   perform:    get termios.c_cc[VTIME]
   return:     VTIME
   comments:   see  NativeEnableReceiveTimeoutThreshold
 * Class:     gnu_io_RXTXPort
 * Method:    NativegetReceiveTimeout
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_gnu_io_RXTXPort_NativegetReceiveTimeout(JNIEnv *env, jobject jobj)
{
  COMMTIMEOUTS CommTimeouts;
  LPCWSTR lpMsgBuf;
  HANDLE hPort = get_fd(env, jobj);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.NativegetReceiveTimeout() called\n");
  )

  // Retrieve the time-out parameters for all read and write operations on the port.
  if (!GetCommTimeouts(hPort, &CommTimeouts))
  { // Unable to get the time-out parameters
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, IO_EXCEPTION, L"NativegetReceiveTimeout - GetCommTimeouts", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    return -1;
  }

  // As far as I know c_cc is byte table
  return CommTimeouts.ReadIntervalTimeout/100 <= 255 ? CommTimeouts.ReadIntervalTimeout / 100 : 255;
}

/*
NativeisReceiveTimeoutEnabled

   accept:     none
   perform:    determine if VTIME is none 0
   return:     JNI_TRUE if VTIME > 0 else JNI_FALSE
   comments:   see  NativeEnableReceiveTimeoutThreshold
 * Class:     gnu_io_RXTXPort
 * Method:    NativeisReceiveTimeoutEnabled
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_NativeisReceiveTimeoutEnabled(JNIEnv *env, jobject jobj)
{
  COMMTIMEOUTS CommTimeouts;
  LPCWSTR lpMsgBuf;
  HANDLE hPort = get_fd(env, jobj);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.NativeisReceiveTimeoutEnabled() called\n");
  )

  // Retrieve the time-out parameters for all read and write operations
  // on the port.
  if (!GetCommTimeouts(hPort, &CommTimeouts))
  { // Unable to get the time-out parameters
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, IO_EXCEPTION, L"NativeisReceiveTimeoutEnabled - GetCommTimeouts", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    return -1;
  }

  return CommTimeouts.ReadIntervalTimeout || CommTimeouts.ReadTotalTimeoutConstant || CommTimeouts.ReadTotalTimeoutMultiplier ? JNI_TRUE : JNI_FALSE;
}

/*
NativeEnableReceiveTimeoutThreshold
   accept:      int  threshold, int vtime,int buffer
   perform:     Set c_cc->VMIN to threshold and c_cc=>VTIME to vtime
   return:      void
   exceptions:  IOException
   comments:    This is actually all handled in read with select in
                canonical input mode.
 * Class:     gnu_io_RXTXPort
 * Method:    NativeEnableReceiveTimeoutThreshold
 * Signature: (III)V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_NativeEnableReceiveTimeoutThreshold(JNIEnv *env, jobject jobj, jint time, jint threshold, jint InputBuffer)
{
  COMMTIMEOUTS CommTimeouts;
  LPCWSTR lpMsgBuf;
  HANDLE hPort = get_fd(env, jobj);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.NativeEnableReceiveTimeoutThreshold(%ld, %ld) called\n", time, threshold);
  )

  // Retrieve the time-out parameters for all read and write operations
  // on the port.
  if (!GetCommTimeouts(hPort, &CommTimeouts))
  { // Unable to get the time-out parameters
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, IO_EXCEPTION, L"NativeEnableReceiveTimeoutThreshold - GetCommTimeouts", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    return;
  }

  /* ------ from javax.comm.CommPort javadoc -------------------------------------------------------------
  |    Threshold   |    Timeout   |Read Buffer Size | Read Behaviour
  |State   |Value  |State   |Value|                 |
  |disabled|  -    |disabled| -   |    n bytes      | block until any data is available
  |enabled |m bytes|disabled| -   |    n bytes      | block until min(m,n) bytes are available
  |disabled|  -    |enabled |x ms |    n bytes      | block for x ms or until any data is available
  |enabled |m bytes|enabled |x ms |    n bytes      | block for x ms or until min(m,n) bytes are available
  --------------------------------------------------------------------------------------------------------

  Enabling the Timeout OR Threshold with a value a zero is a special case.
  This causes the underlying driver to poll for incoming data instead being event driven.
  Otherwise, the behaviour is identical to having both the Timeout and Threshold disabled.
  */

  // Following is based on my understanding of timeout parameters meaning.
  // Not completely precise (threshold?!)

  if(time == 0)
  { // polling mode - return if no data
    CommTimeouts.ReadIntervalTimeout = MAXDWORD;
    CommTimeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
    CommTimeouts.ReadTotalTimeoutConstant = 1;
  }
  else if(time == -1)
  { // disable timeout
    CommTimeouts.ReadIntervalTimeout = 0;
    CommTimeouts.ReadTotalTimeoutMultiplier = 0;
    CommTimeouts.ReadTotalTimeoutConstant = 0;
  }
  else
  { // set timeout
    CommTimeouts.ReadIntervalTimeout = 0;
    CommTimeouts.ReadTotalTimeoutMultiplier = 0;
    CommTimeouts.ReadTotalTimeoutConstant = time;
  }

  // Set the time-out parameters for all read and write operations
  // on the port.
  if (!SetCommTimeouts (hPort, &CommTimeouts))
  { // Unable to set the time-out parameters
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, IO_EXCEPTION, L"NativeEnableReceiveTimeoutThreshold - SetCommTimeouts", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    return;
  }
}

/*
isDTR

   accept:      none
   perform:     check status of DTR
   return:      true if TIOCM_DTR is set
                false if TIOCM_DTR is not set
   exceptions:  none
   comments:    DTR stands for Data Terminal Ready
 * Class:     gnu_io_RXTXPort
 * Method:    isDTR
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_isDTR(JNIEnv *env, jobject jobj)
{
  DCB PortDCB;
  HANDLE hPort = get_fd(env, jobj);

  PortDCB.DCBlength = sizeof(DCB);

  // Get the default port setting information.
  if(!GetCommState(hPort, &PortDCB))
  {
    GetLastError();
    return JNI_FALSE;
  }

  // This is not completely correct: I assume that nobody's playing with
  // manually setting/resetting DTR. Instead, DTR is in handshake mode (ON unless
  // port closed) or disable mode (OFF).
  // So, don't use EscapeCommFunction(SETDTR/CLRDTR) in this library!
  return PortDCB.fDtrControl != DTR_CONTROL_DISABLE ? JNI_TRUE : JNI_FALSE;
}

/*
setDTR

   accept:      new DTR state
   perform:     if state is true, TIOCM_DTR is set
                if state is false, TIOCM_DTR is unset
   return:      none
   exceptions:  none
   comments:    DTR stands for Data Terminal Ready
 * Class:     gnu_io_RXTXPort
 * Method:    setDTR
 * Signature: (Z)V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_setDTR(JNIEnv *env, jobject jobj, jboolean state)
{
  DCB PortDCB;

  HANDLE hPort = get_fd(env, jobj);
  PortDCB.DCBlength = sizeof(DCB);

  // Don't use EscapeCommFunction(SETDTR/CLRDTR) in this library!

  // Get the default port setting information.
  if(!GetCommState (hPort, &PortDCB))
  { //Unable to get configuration of the serial port
    GetLastError();
    return;
  }

  if(state == JNI_TRUE)
    PortDCB.fDtrControl = DTR_CONTROL_HANDSHAKE;
  else
    PortDCB.fDtrControl = DTR_CONTROL_DISABLE;


  // Configure the port according to the specifications of the DCB structure.
  if (!SetCommState (hPort, &PortDCB))
  { //Unable to configure the serial port
    GetLastError();
    return;
  }
}

/*
setRTS

   accept:      state  flag to set/unset.
   perform:     depends on the state flag
                if true TIOCM_RTS is set
                if false TIOCM_RTS is unset
   return:      none
   exceptions:  none
   comments:    tcsetattr with c_cflag CRTS_IFLOW
 * Class:     gnu_io_RXTXPort
 * Method:    setRTS
 * Signature: (Z)V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_setRTS(JNIEnv *env, jobject jobj, jboolean state)
{
  HANDLE hPort = get_fd(env, jobj);

  // EscapeCommFunction will fail if there's RTS/CTS flowcontrol. If it is incorrect,
  // we could turn flowcontrol off when getting to this function
  if(state == JNI_TRUE)
    EscapeCommFunction(hPort, SETRTS);
  else
    EscapeCommFunction(hPort, CLRRTS);
}

/*
setDSR

   accept:      state  flag to set/unset.
   perform:     depends on the state flag
                if true TIOCM_DSR is set
                if false TIOCM_DSR is unset
   return:      none
   exceptions:  none
   comments:    tcsetattr with c_cflag CRTS_IFLOW
 * Class:     gnu_io_RXTXPort
 * Method:    setDSR
 * Signature: (Z)V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_setDSR(JNIEnv *env, jobject jobj, jboolean state)
{
  DCB PortDCB;

  HANDLE hPort = get_fd(env, jobj);
  PortDCB.DCBlength = sizeof(DCB);

  // Get the default port setting information.
  if(!GetCommState (hPort, &PortDCB))
  { //Unable to get configuration of the serial port
    GetLastError();
    return;
  }

  // Change the DCB structure settings.
  if(state != JNI_FALSE)
    PortDCB.fOutxDsrFlow = TRUE;
  else
    PortDCB.fOutxDsrFlow = FALSE;

  // Configure the port according to the specifications of the DCB
  // structure.
  if (!SetCommState (hPort, &PortDCB))
  { //Unable to configure the serial port
    GetLastError();
    return;
  }
}

/*
isCTS

   accept:      none
   perform:     check status of CTS
   return:      true if TIOCM_CTS is set
                false if TIOCM_CTS is not set
   exceptions:  none
   comments:    CTS stands for Clear To Send.
 * Class:     gnu_io_RXTXPort
 * Method:    isCTS
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_isCTS(JNIEnv *env, jobject jobj)
{
  DWORD ModemStat;
  HANDLE hPort = get_fd(env, jobj);

  if(!GetCommModemStatus(hPort, &ModemStat))
    return JNI_FALSE;

  return ModemStat|MS_CTS_ON ? JNI_TRUE : JNI_FALSE;
}

/*
isDSR

   accept:      none
   perform:     check status of DSR
   return:      true if TIOCM_DSR is set
                false if TIOCM_DSR is not set
   exceptions:  none
   comments:    DSR stands for Data Set Ready
 * Class:     gnu_io_RXTXPort
 * Method:    isDSR
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_isDSR(JNIEnv *env, jobject jobj)
{
  DWORD ModemStat;
  HANDLE hPort = get_fd(env, jobj);

  if(!GetCommModemStatus(hPort, &ModemStat))
    return JNI_FALSE;

  return ModemStat|MS_DSR_ON ? JNI_TRUE : JNI_FALSE;
}

/*
isCD

   accept:      none
   perform:     check status of CD
   return:      true if TIOCM_CD is set
                false if TIOCM_CD is not set
   exceptions:  none
   comments:    CD stands for Carrier Detect
                The following comment has been made...
                "well, it works, there might ofcourse be a bug, but making DCD
                permanently on fixed it for me so I don't care"
 * Class:     gnu_io_RXTXPort
 * Method:    isCD
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_isCD(JNIEnv *env, jobject jobj)
{
  DWORD ModemStat;
  HANDLE hPort = get_fd(env, jobj);

  if(!GetCommModemStatus(hPort, &ModemStat))
    return JNI_FALSE;

  return ModemStat|MS_RLSD_ON ? JNI_TRUE : JNI_FALSE;
}

/*
isRI

   accept:      none
   perform:     check status of RI
   return:      true if TIOCM_RI is set
                false if TIOCM_RI is not set
   exceptions:  none
   comments:    RI stands for Ring Indicator
 * Class:     gnu_io_RXTXPort
 * Method:    isRI
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_isRI(JNIEnv *env, jobject jobj)
{
  DWORD ModemStat;
  HANDLE hPort = get_fd(env, jobj);

  if(!GetCommModemStatus(hPort, &ModemStat))
    return JNI_FALSE;

  return ModemStat|MS_RING_ON ? JNI_TRUE : JNI_FALSE;
}

/*
isRTS

   accept:      none
   perform:     check status of RTS
   return:      true if TIOCM_RTS is set
                false if TIOCM_RTS is not set
   exceptions:  none
   comments:    tcgetattr with c_cflag CRTS_IFLOW
 * Class:     gnu_io_RXTXPort
 * Method:    isRTS
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_isRTS(JNIEnv *env, jobject jobj)
{
  DCB PortDCB;
  HANDLE hPort = get_fd(env, jobj);

  PortDCB.DCBlength = sizeof(DCB);
  if(!GetCommState (hPort, &PortDCB))
  { //Unable to get configuration of the serial port
    GetLastError();
    return JNI_FALSE;
  }

  if(PortDCB.fRtsControl == RTS_CONTROL_DISABLE)
    return JNI_FALSE;

  if(PortDCB.fRtsControl == RTS_CONTROL_ENABLE)
    return JNI_TRUE;

  if(PortDCB.fRtsControl == RTS_CONTROL_HANDSHAKE)
  {
    //MessageBox(NULL, L"Undefined condition: isRTS() but control is RTS_CONTROL_HANDSHAKE\r\nReturning FALSE", L"Warning", MB_OK | MB_ICONWARNING | MB_SETFOREGROUND);
    printj(env, L"Undefined condition: isRTS() but control is RTS_CONTROL_HANDSHAKE. Returning FALSE\n");
    return JNI_FALSE;
  }

  return JNI_FALSE;
}

/*
sendBreak

   accept:     duration in milliseconds.
   perform:    send break for actual time.  not less than 0.25 seconds.
   exceptions: none
   comments:   not very precise
 * Class:     gnu_io_RXTXPort
 * Method:    sendBreak
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_sendBreak(JNIEnv *env, jobject jobj, jint duration)
{
  HANDLE hPort = get_fd(env, jobj);

  SetCommBreak(hPort);
  Sleep(duration);
  ClearCommBreak(hPort);
}

/*
writeByte

   accept:      byte to write (passed as int)
   perform:     write a single byte to the port
   return:      none
   exceptions:  IOException
 * Class:     gnu_io_RXTXPort
 * Method:    writeByte
 * Signature: (IZ)V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_writeByte(JNIEnv *env, jobject jobj, jint b, jboolean i)
{
  DWORD dwNumBytesWritten;
  LPCWSTR lpMsgBuf;
  HANDLE hPort = get_fd(env, jobj);
  BYTE bb = (BYTE)b;

  do {
    if (!WriteFile(hPort,              // Port handle
                   &bb,                // Pointer to the data to write
                   1,                  // Number of bytes to write
                   &dwNumBytesWritten, // Pointer to the number of bytes written
                   NULL))              // Must be NULL for Windows CE
    { // WriteFile failed. Report error.
      CreateErrorMsg(GetLastError(), lpMsgBuf);
      throw_java_exceptionW(env, IO_EXCEPTION, L"writeByte - WriteFile", lpMsgBuf );
      // Free the buffers.
      ReleaseErrorMsg(lpMsgBuf);
      return;
    }
  } while(dwNumBytesWritten == 0);
}

/*
writeArray

   accept:      jbarray: bytes used for writing
                off: offset in array to start writing
                len: Number of bytes to write
   perform:     write length bytes of jbarray
   return:      none
   exceptions:  IOException
 * Class:     gnu_io_RXTXPort
 * Method:    writeArray
 * Signature: ([BIIZ)V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_writeArray(JNIEnv *env, jobject jobj, jbyteArray b, jint off, jint len, jboolean i)
{
  LPCWSTR lpMsgBuf;
	DWORD dwNumBytesWritten;
  jint total=0;
  HANDLE hPort = get_fd(env, jobj);

  jbyte *body = env->GetByteArrayElements(b, NULL);

	do {
    IF_DEBUG
    (
      printj(env, L"--- writeArray - %d bytes to write\n", len-total);
    )
    if (!WriteFile(hPort,              // Port handle
                   body+total+off,     // Pointer to the data to write
                   len-total,          // Number of bytes to write
                   &dwNumBytesWritten, // Pointer to the number of bytes written
                   NULL))              // Must be NULL for Windows CE
    { // WriteFile failed. Report error.
      CreateErrorMsg(GetLastError(), lpMsgBuf);
      throw_java_exceptionW(env, IO_EXCEPTION, L"writeArray - WriteFile", lpMsgBuf );
      // Free the buffers.
      ReleaseErrorMsg(lpMsgBuf);
      env->ReleaseByteArrayElements(b, body, JNI_ABORT);
      return;
    }
    total += dwNumBytesWritten;
	} while( total < len );

  env->ReleaseByteArrayElements(b, body, JNI_ABORT);
}

/*
nativeDrain

   accept:      none
   perform:     wait until all data is transmitted
   return:      none
   exceptions:  IOException
   comments:    java.io.OutputStream.flush() is equivalent to tcdrain,
                not tcflush, which throws away unsent bytes

                count logic added to avoid infinite loops when EINTR is
                true...  Thread.yeild() was suggested.
 * Class:     gnu_io_RXTXPort
 * Method:    nativeDrain
 * Signature: ()V
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_nativeDrain(JNIEnv *env, jobject jobj, jboolean i)
{
  //COMSTAT Stat;
  //DWORD dwErrors;
  HANDLE hPort = get_fd(env, jobj);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.nativeDrain() called\n");
  )
  /*
  if(!FlushFileBuffers(hPort))
  {
    IF_DEBUG
    (
      printj(env, L"!!! FlushFileBuffers() error %ld\n", GetLastError());
    )
    return JNI_FALSE;
  }
   */
  // Alternative implementation:
  /*
  do
  {
    if(!ClearCommError(hPort, &dwErrors, &Stat))
    {
      GetLastError();
      return;
    }
    Sleep(10);
  } while(Stat.cbOutQue > 0);*/

  return JNI_FALSE;
}

/*
nativeavailable

   accept:      none
   perform:     find out the number of bytes available for reading
   return:      available bytes
                -1 on error
   exceptions:  none /// should be IOException
 * Class:     gnu_io_RXTXPort
 * Method:    nativeavailable
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_gnu_io_RXTXPort_nativeavailable(JNIEnv *env, jobject jobj)
{
  DWORD dwErrors;
  COMSTAT Stat;
  LPCWSTR lpMsgBuf;
  HANDLE hPort = get_fd(env, jobj);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.nativeavailable() called");
  )

  if(!ClearCommError(hPort, &dwErrors, &Stat))
  {
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, IO_EXCEPTION, L"nativeavailable - ClearCommError", lpMsgBuf );
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    return -1;
  }

  IF_DEBUG
  (
    printj(env, L" - returning %ld\n", Stat.cbInQue);
  )

  return Stat.cbInQue;
}

/*
readByte

   accept:      none
   perform:     Read a single byte from the port
   return:      The byte read
   exceptions:  IOException
 * Class:     gnu_io_RXTXPort
 * Method:    readByte
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_gnu_io_RXTXPort_readByte(JNIEnv *env, jobject jobj)
{
  BYTE bb;
  DWORD dwNumBytesRead;
  LPCWSTR lpMsgBuf;
  HANDLE hPort = get_fd(env, jobj);

  if (!ReadFile(hPort, &bb, 1, &dwNumBytesRead, NULL))
  { // ReadFile failed. Report error.
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    throw_java_exceptionW(env, IO_EXCEPTION, L"readByte - ReadFile", lpMsgBuf);
    // Free the buffers.
    ReleaseErrorMsg(lpMsgBuf);
    return -1;
  }

  return dwNumBytesRead > 0 ? bb : -1;
}

/*
readArray

   accept:       offset (offset to start storing data in the jbarray) and
                 Length (bytes to read)
   perform:      read bytes from the port into a byte array
   return:       bytes read on success
                 0 on read timeout
   exceptions:   IOException
   comments:     throws ArrayIndexOutOfBoundsException if asked to
                 read more than SSIZE_MAX bytes
 * Class:     gnu_io_RXTXPort
 * Method:    readArray
 * Signature: ([BII)I
 */
JNIEXPORT jint JNICALL Java_gnu_io_RXTXPort_readArray(JNIEnv *env, jobject jobj, jbyteArray b, jint off, jint len)
{
  LPCWSTR lpMsgBuf;
	DWORD dwNumBytesRead, dwTotalRead = 0;
  jint threshold;
  HANDLE hPort = get_fd(env, jobj);

  if(len < 0)
  {
    throw_java_exceptionW(env, ARRAY_INDEX_OUT_OF_BOUNDS, L"readArray", L"Negative number of character to read");
    return -1;
  }

  jbyte *body = env->GetByteArrayElements(b, NULL);
  threshold = get_java_int_var(env, jobj, "threshold");

  do
  {
    if (!ReadFile(hPort, (unsigned char *)(body+off), len, &dwNumBytesRead, NULL))
    { // ReadFile failed. Report error.
      CreateErrorMsg(GetLastError(), lpMsgBuf);
      throw_java_exceptionW(env, IO_EXCEPTION, L"readArray - WriteFile", lpMsgBuf );
      // Free the buffers.
      ReleaseErrorMsg(lpMsgBuf);
      env->ReleaseByteArrayElements(b, body, JNI_ABORT);
      return -1;
    }

    dwTotalRead += dwNumBytesRead;
  } while(dwNumBytesRead > 0 && threshold != 0 && dwTotalRead <= (DWORD)len && dwTotalRead < (DWORD)threshold);

  env->ReleaseByteArrayElements(b, body, 0);
  return dwTotalRead;
}

/*
eventLoop

   accept:      none
   perform:     periodically check for SerialPortEvents
   return:      none
   exceptions:  none
   comments:	please keep this function clean.
 * Class:     gnu_io_RXTXPort
 * Method:    eventLoop
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_eventLoop(JNIEnv *env, jobject jobj)
{
  jfieldID jfMonitorThreadCloseLock, jfMonitorThreadLock, jfMonThreadisInterrupted;
  jmethodID jmSendEvent;
  HANDLE hCommEventThread;
  DWORD dwThreadID, dwWaitResult, dwEvent;
  int RetVal;
  HANDLE hPort = get_fd(env, jobj);
  EventInfoStruct *EventInfo = get_eis(env, jobj);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.eventLoop() start\n");
  )

  jclass cls = env->GetObjectClass(jobj);

  // Get pointers to some Java variables and methods:
  if( !(jfMonitorThreadLock = env->GetFieldID(cls, "MonitorThreadLock", "Z")) ||
      !(jfMonThreadisInterrupted = env->GetFieldID(cls, "monThreadisInterrupted", "Z")) ||
      !(jfMonitorThreadCloseLock = env->GetFieldID(cls, "MonitorThreadCloseLock", "Z")) ||
      !(jmSendEvent = env->GetMethodID(cls, "sendEvent", "(IZ)Z"))
    )
  {
    IF_DEBUG
    (
		  env->ExceptionDescribe();
    )
	  env->ExceptionClear();
	  return;
  }

  hCommEventThread = CreateThread(NULL, 0, CommEventThread, (LPVOID)EventInfo, 0, &dwThreadID);
  if(hCommEventThread == NULL)
  {
    LPCWSTR lpMsgBuf;
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    printj(env, L"!!! eventLoop - CreateThread() error: %s\n", lpMsgBuf);
    ReleaseErrorMsg(lpMsgBuf);
    return;
  }

  CloseHandle(hCommEventThread);

  do
  {
    if(env->GetBooleanField(jobj, jfMonThreadisInterrupted) == JNI_TRUE)
    {
      IF_DEBUG
      (
        printj(env, L"--- RXTXPort.eventLoop() interrupted - exiting\n");
      )

      CloseHandle(EventInfo->eventHandle);

	    env->SetBooleanField(jobj, jfMonitorThreadCloseLock, JNI_FALSE);
      return;
    }

    if(EventInfo->eventThreadReady)
    { // Thread is ready to work - pass signal to Java
      IF_DEBUG
      (
        printj(env, L"--- RXTXPort.eventLoop() - EventThread is ready\n");
      )
      env->SetBooleanField(jobj, jfMonitorThreadLock, JNI_FALSE);
      EventInfo->eventThreadReady = false;
    }

    if((dwWaitResult = WaitForSingleObject(EventInfo->eventHandle, 250)) == WAIT_FAILED)
    {
      IF_DEBUG
      (
        LPCWSTR lpMsgBuf;
        CreateErrorMsg(GetLastError(), lpMsgBuf);
        printj(env, L"!!! eventLoop - WaitForSingleObject() error: %s\n", lpMsgBuf);
        ReleaseErrorMsg(lpMsgBuf);
      )
      CloseHandle(EventInfo->eventHandle);
	  env->SetBooleanField(jobj, jfMonitorThreadCloseLock, JNI_FALSE);
      return;
    }

    if(dwWaitResult == WAIT_OBJECT_0)
    {
      dwEvent = EventInfo->event;
      // Clearing event - event thread will continue
      EventInfo->event = 0;
      // Send events to Java
      if(RetVal = SendEvents(env, jobj, dwEvent, EventInfo, jmSendEvent))
      {
        IF_DEBUG
        (
          printj(env, L"!!! eventLoop - SendEvents() result: %d\n", RetVal);
        )
        CloseHandle(EventInfo->eventHandle);
  	    env->SetBooleanField(jobj, jfMonitorThreadCloseLock, JNI_FALSE);
        return;
      }
    }
    else
    {
      IF_DEBUG
      (
        //printj(env, L"--- eventLoop() looping\n");
        MessageBeep(MB_OK);
      )
    }
  } while(TRUE);
}


/*
interruptEventLoop

   accept:      nothing
   perform:     sets monThreadisInterrupted
   return:      nothing
   exceptions:  none
   comments:    real monitoring thread will stay until port is closed
 * Class:     gnu_io_RXTXPort
 * Method:    interruptEventLoop
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_interruptEventLoop(JNIEnv *env, jobject jobj)
{
  jfieldID jfid;
  HANDLE hPort = get_fd(env, jobj);

  jclass cls = env->GetObjectClass(jobj);
  jfid = env->GetFieldID(cls, "monThreadisInterrupted", "Z");
	env->SetBooleanField(jobj, jfid, JNI_TRUE);
}


/*
 nativeSetEventFlag

   accept:      fd for finding the struct, event to flag, flag.
   perform:     toggle the flag
   return:      none
   exceptions:  none
   comments:	all the logic used to be done in Java but its too noisy
 * Class:     gnu_io_RXTXPort
 * Method:    nativeSetEventFlag
 * Signature: (IIZ)V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_nativeSetEventFlag(JNIEnv *env, jobject jobj, jint fd, jint event, jboolean flag)
{
  DWORD dwErr, NewFlag;
  EventInfoStruct *EventInfo = get_eis(env, jobj);
  HANDLE hPort = (HANDLE)fd;

  IF_DEBUG
  (
    printj(env, L"--- nativeSetEventFlag(%ld, %d) called\n", event, (int)flag);
  )

  switch(event)
  {
    case SPE_DATA_AVAILABLE:
      NewFlag = EV_RXCHAR;
    break;

    case SPE_OUTPUT_BUFFER_EMPTY:
      NewFlag = EV_TXEMPTY;
    break;

    case SPE_CTS:
      NewFlag = EV_CTS;
    break;

    case SPE_DSR:
      NewFlag = EV_DSR;
    break;

    case SPE_RI:
      NewFlag = EV_RING;
    break;

    case SPE_CD:
      NewFlag = EV_RLSD;
    break;

    case SPE_OE:
    case SPE_PE:
    case SPE_FE:
      NewFlag = EV_ERR;
    break;

    case SPE_BI:
      NewFlag = EV_BREAK;
    break;

  }

  if(flag == JNI_TRUE)
    EventInfo->ef = EventInfo->ef | NewFlag;
  else
    EventInfo->ef = EventInfo->ef & ~NewFlag;

  if(!SetCommMask(hPort, EventInfo->ef))
  {
    dwErr = GetLastError();
    IF_DEBUG
    (
      LPCWSTR lpMsgBuf;
      CreateErrorMsg(dwErr, lpMsgBuf);
      printj(env, L"!!! nativeSetEventFlag - SetCommMask() error: %ld %s\n", dwErr, lpMsgBuf );
      ReleaseErrorMsg(lpMsgBuf);
    )
    return;
  }

}

/*
nativeClose

   accept:      none
   perform:     get the fd from the java end and close it
   return:      none
   exceptions:  none
 * Class:     gnu_io_RXTXPort
 * Method:    nativeClose
 * Signature: (Ljava/lang/String{ })V
 */
JNIEXPORT void JNICALL Java_gnu_io_RXTXPort_nativeClose(JNIEnv *env, jobject jobj, jstring name)
{
  HANDLE hPort = get_fd(env, jobj);

  IF_DEBUG
  (
    LPCWSTR wszName = env->GetStringChars(name, NULL);
    printj(env, L"--- RXTXPort.nativeClose(%s) called\n", wszName);
    env->ReleaseStringChars(name, wszName);
  )

  if (!CloseHandle(hPort))
  {
    GetLastError();
    return;
  }

}

/*
nativeGetParityErrorChar

   accept:      -
   perform:     check the ParityErrorChar
   return:      The ParityErrorChar as an jbyte.
   exceptions:  UnsupportedCommOperationException if not implemented
   comments:    It appears the Parity char is usually \0.  The windows
		API allows for this to be changed.  I cant find may
		examples of this being done.  Maybe for a reason.

		Use a direct call to the termios file until we find a
		solution.
 * Class:     gnu_io_RXTXPort
 * Method:    nativeGetParityErrorChar
 * Signature: ()I
 */
JNIEXPORT jbyte JNICALL Java_gnu_io_RXTXPort_nativeGetParityErrorChar(JNIEnv *env, jobject jobj)
{
  DCB PortDCB;
  LPCWSTR lpMsgBuf;

  HANDLE hPort = get_fd(env, jobj);
  PortDCB.DCBlength = sizeof(DCB);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.nativeGetParityErrorChar() called\n");
  )

  // Get port setting information.
  if(!GetCommState(hPort, &PortDCB))
  { //Unable to get configuration of the serial port
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    IF_DEBUG
    (
      printj(env, L"!!! nativeGetParityErrorChar - GetCommState() error: %s\n", lpMsgBuf);
    )
    ReleaseErrorMsg(lpMsgBuf);
    return -1;
  }

  return (jbyte)PortDCB.ErrorChar;
}

/*
nativeSetParityErrorChar

   accept:      the ParityArrorCharacter as an int.
   perform:     Set the ParityErrorChar
   return:      JNI_TRUE on success
   exceptions:  UnsupportedCommOperationException if not implemented
   comments:    It appears the Parity char is usually \0.  The windows
		API allows for this to be changed.  I cant find may
		examples of this being done.  Maybe for a reason.

		Use a direct call to the termios file until we find a
		solution.
 * Class:     gnu_io_RXTXPort
 * Method:    nativeSetParityErrorChar
 * Signature: (B)Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_nativeSetParityErrorChar(JNIEnv *env, jobject jobj, jbyte b)
{
  DCB PortDCB;
  LPCWSTR lpMsgBuf;

  HANDLE hPort = get_fd(env, jobj);
  PortDCB.DCBlength = sizeof(DCB);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.nativeSetParityErrorChar(%#x) called\n", (int)b);
  )

  // Get port setting information.
  if(!GetCommState(hPort, &PortDCB))
  { //Unable to get configuration of the serial port
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    IF_DEBUG
    (
      printj(env, L"!!! nativeSetParityErrorChar - GetCommState() error: %s\n", lpMsgBuf);
    )
    ReleaseErrorMsg(lpMsgBuf);
    return JNI_FALSE;
  }

  if(b != 0)
  {
    PortDCB.fErrorChar = TRUE;
    PortDCB.fParity = TRUE;
    PortDCB.ErrorChar = b;
  }
  else
  {
    PortDCB.fErrorChar = FALSE;
    PortDCB.fParity = FALSE;
    PortDCB.ErrorChar = 0;
  }

  if (!SetCommState(hPort, &PortDCB))
  {
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    IF_DEBUG
    (
      printj(env, L"!!! nativeSetParityErrorChar - SetCommState() error: %s\n", lpMsgBuf);
    )
    ReleaseErrorMsg(lpMsgBuf);
    return JNI_FALSE;
  }

  return JNI_TRUE;
}

/*
nativeGetEndOfInputChar

   accept:      -
   perform:     check the EndOf InputChar
   return:      the EndOfInputChar as an jbyte.  -1 on error
   exceptions:  UnsupportedCommOperationException if not implemented
   comments:
 * Class:     gnu_io_RXTXPort
 * Method:    nativeGetEndOfInputChar
 * Signature: ()I
 */
JNIEXPORT jbyte JNICALL Java_gnu_io_RXTXPort_nativeGetEndOfInputChar(JNIEnv *env, jobject jobj)
{
  DCB PortDCB;
  LPCWSTR lpMsgBuf;

  HANDLE hPort = get_fd(env, jobj);
  PortDCB.DCBlength = sizeof(DCB);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.nativeGetEndOfInputChar() called\n");
  )

  // Get port setting information.
  if(!GetCommState(hPort, &PortDCB))
  { //Unable to get configuration of the serial port
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    IF_DEBUG
    (
      printj(env, L"!!! nativeGetEndOfInputChar - GetCommState() error: %s\n", lpMsgBuf);
    )
    ReleaseErrorMsg(lpMsgBuf);
    return -1;
  }

  return (jbyte)PortDCB.EofChar;
}


/*
nativeSetEndOfInputChar

   accept:      The EndOfInputChar as an int
   perform:     set the EndOfInputChar
   return:      JNI_TRUE on success
   exceptions:  UnsupportedCommOperationException if not implemented
   comments:    This may cause troubles on Windows.
		Lets give it a shot and see what happens.

		See termios.c for the windows bits.

		EofChar = val;
		fBinary = false;  //winapi docs say always use true. ?
 * Class:     gnu_io_RXTXPort
 * Method:    nativeSetEndOfInputChar
 * Signature: (B)Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_nativeSetEndOfInputChar(JNIEnv *env, jobject jobj, jbyte b)
{
  DCB PortDCB;
  LPCWSTR lpMsgBuf;

  HANDLE hPort = get_fd(env, jobj);
  PortDCB.DCBlength = sizeof(DCB);

  IF_DEBUG
  (
    printj(env, L"--- RXTXPort.nativeSetEndOfInputChar(%#x) called\n", (int)b);
  )

  // Get port setting information.
  if(!GetCommState(hPort, &PortDCB))
  { //Unable to get configuration of the serial port
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    IF_DEBUG
    (
      printj(env, L"!!! nativeSetEndOfInputChar - GetCommState() error: %s\n", lpMsgBuf);
    )
    ReleaseErrorMsg(lpMsgBuf);
    return JNI_FALSE;
  }

  if( b != 0)
  {
    PortDCB.fBinary = FALSE;
    PortDCB.EofChar = b;
  }
  else
  {
    PortDCB.fBinary = TRUE;
    PortDCB.EofChar = 0;
  }

  if (!SetCommState(hPort, &PortDCB))
  {
    CreateErrorMsg(GetLastError(), lpMsgBuf);
    IF_DEBUG
    (
      printj(env, L"!!! nativeSetEndOfInputChar - SetCommState() error: %s\n", lpMsgBuf);
    )
    throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeSetEndOfInputChar", lpMsgBuf);
    ReleaseErrorMsg(lpMsgBuf);
    return JNI_FALSE;
  }

  return JNI_TRUE;
}


/*
 * Class:     gnu_io_RXTXPort
 * Method:    nativeSetUartType
 * Signature: (Ljava/lang/String{ }Z)Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_nativeSetUartType(JNIEnv *env, jobject jobj, jstring type, jboolean test)
{
  throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeSetUartType", L"Operation not implemented");
  return JNI_FALSE;
}

/*
 * Class:     gnu_io_RXTXPort
 * Method:    nativeGetUartType
 * Signature: ()Ljava/lang/String{ }
 */
JNIEXPORT jstring JNICALL Java_gnu_io_RXTXPort_nativeGetUartType(JNIEnv *env, jobject jobj)
{
  throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeSetUartType", L"Operation not implemented");
  return env->NewStringUTF("Unknown");
}

/*
 * Class:     gnu_io_RXTXPort
 * Method:    nativeSetBaudBase
 * Signature: (I)Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_nativeSetBaudBase(JNIEnv *env, jobject jobj, jint baudBase)
{
  throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeSetBaudBase", L"Operation not implemented");
  return JNI_FALSE;
}

/*
 * Class:     gnu_io_RXTXPort
 * Method:    nativeGetBaudBase
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_gnu_io_RXTXPort_nativeGetBaudBase(JNIEnv *env, jobject jobj)
{
  throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeGetBaudBase", L"Operation not implemented");
  return -1;
}

/*
 * Class:     gnu_io_RXTXPort
 * Method:    nativeSetDivisor
 * Signature: (I)Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_nativeSetDivisor(JNIEnv *env, jobject jobj, jint divisor)
{
  throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeSetDivisor", L"Operation not implemented");
  return JNI_FALSE;
}

/*
 * Class:     gnu_io_RXTXPort
 * Method:    nativeGetDivisor
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_gnu_io_RXTXPort_nativeGetDivisor(JNIEnv *env, jobject jobj)
{
  throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeGetDivisor", L"Operation not implemented");
  return -1;
}

/*
 * Class:     gnu_io_RXTXPort
 * Method:    nativeSetLowLatency
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_nativeSetLowLatency(JNIEnv *env, jobject jobj)
{
  throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeSetLowLatency", L"Operation not implemented");
  return JNI_FALSE;
}

/*
 * Class:     gnu_io_RXTXPort
 * Method:    nativeGetLowLatency
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_nativeGetLowLatency(JNIEnv *env, jobject jobj)
{
  throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeGetLowLatency", L"Operation not implemented");
  return JNI_FALSE;
}

/*
 * Class:     gnu_io_RXTXPort
 * Method:    nativeSetCallOutHangup
 * Signature: (Z)Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_nativeSetCallOutHangup(JNIEnv *env, jobject jobj, jboolean noHup)
{
  throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeSetCallOutHangup", L"Operation not implemented");
  return JNI_FALSE;
}

/*
 * Class:     gnu_io_RXTXPort
 * Method:    nativeGetCallOutHangup
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_gnu_io_RXTXPort_nativeGetCallOutHangup(JNIEnv *env, jobject jobj)
{
  throw_java_exceptionW(env, UNSUPPORTED_COMM_OPERATION, L"nativeGetCallOutHangup", L"Operation not implemented");
  return JNI_FALSE;
}

