/*-------------------------------------------------------------------------
|   RXTX License v 2.1 - LGPL v 2.1 + Linking Over Controlled Interface.
|   RXTX is a native interface to serial ports in java.
|   Copyright 1997-2007 by Trent Jarvi tjarvi@qbang.org and others who
|   actually wrote it.  See individual source files for more information.
|
|   A copy of the LGPL v 2.1 may be found at
|   http://www.gnu.org/licenses/lgpl.txt on March 4th 2007.  A copy is
|   here for your convenience.
|
|   This library is free software; you can redistribute it and/or
|   modify it under the terms of the GNU Lesser General Public
|   License as published by the Free Software Foundation; either
|   version 2.1 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
|   Lesser General Public License for more details.
|
|   An executable that contains no derivative of any portion of RXTX, but
|   is designed to work with RXTX by being dynamically linked with it,
|   is considered a "work that uses the Library" subject to the terms and
|   conditions of the GNU Lesser General Public License.
|
|   The following has been added to the RXTX License to remove
|   any confusion about linking to RXTX.   We want to allow in part what
|   section 5, paragraph 2 of the LGPL does not permit in the special
|   case of linking over a controlled interface.  The intent is to add a
|   Java Specification Request or standards body defined interface in the 
|   future as another exception but one is not currently available.
|
|   http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
|
|   As a special exception, the copyright holders of RXTX give you
|   permission to link RXTX with independent modules that communicate with
|   RXTX solely through the Sun Microsytems CommAPI interface version 2,
|   regardless of the license terms of these independent modules, and to copy
|   and distribute the resulting combined work under terms of your choice,
|   provided that every copy of the combined work is accompanied by a complete
|   copy of the source code of RXTX (the version of RXTX used to produce the
|   combined work), being distributed under the terms of the GNU Lesser General
|   Public License plus this exception.  An independent module is a
|   module which is not derived from or based on RXTX.
|
|   Note that people who make modified versions of RXTX are not obligated
|   to grant this special exception for their modified versions; it is
|   their choice whether to do so.  The GNU Lesser General Public License
|   gives permission to release a modified version without this exception; this
|   exception also makes it possible to release a modified version which
|   carries forward this exception.
|
|   You should have received a copy of the GNU Lesser 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
|   All trademarks belong to their respective owners.
--------------------------------------------------------------------------*/
package gnu.io;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.TooManyListenersException;
import java.lang.Math;

/**
* An extension of gnu.io.SerialPort
* @see gnu.io.SerialPort
*/

final public class RXTXPort extends SerialPort
{
	/* I had a report that some JRE's complain when MonitorThread
	   tries to access private variables
	*/

	protected final static boolean debug = false;
	protected final static boolean debug_read = false;
	protected final static boolean debug_read_results = false;
	protected final static boolean debug_write = false;
	protected final static boolean debug_events = false;
	protected final static boolean debug_verbose = false;

	private static Zystem z;

	static
	{
		try {
			z = new Zystem( Zystem.PRINT_MODE );
		} catch ( Exception e ) {};

		if(debug ) 
			z.reportln( "RXTXPort {}");
/*
		System.loadLibrary( "rxtxSerial" );
*/
		Initialize();
	}

	/** Initialize the native library */
	private native static void Initialize();
	boolean MonitorThreadAlive=false;

	/** 
	*  Open the named port
	*  @param name the name of the device to open
	*  @throws  PortInUseException
	*  @see gnu.io.SerialPort
	*/
	public RXTXPort( String name ) throws PortInUseException
	{
		if (debug)
			z.reportln( "RXTXPort:RXTXPort("+name+") called");
	/* 
	   commapi/javadocs/API_users_guide.html specifies that whenever
	   an application tries to open a port in use by another application
	   the PortInUseException will be thrown

	   I know some didnt like it this way but I'm not sure how to avoid
	   it.  We will just be writing to a bogus fd if we catch the 
	   exeption

	   Trent
	*/
	//	try {
			fd = open( name );
			this.name = name;

			MonitorThreadLock = true;
			monThread = new MonitorThread();
			monThread.start();
			waitForTheNativeCodeSilly();
			MonitorThreadAlive=true;
	//	} catch ( PortInUseException e ){}
		timeout = -1;	/* default disabled timeout */
		if (debug)
			z.reportln( "RXTXPort:RXTXPort("+name+") returns with fd = " +
				fd);
	}
	private native synchronized int open( String name )
		throws PortInUseException;

	/** File descriptor */
	protected int fd = 0;
	/** a pointer to the event info structure used to share information
	    between threads so write threads can send output buffer empty
	    from a pthread if need be.
	*/
	int eis = 0;
	/** pid for lock files */
	int pid = 0;

	/** DSR flag **/
	static boolean dsrFlag = false;

	/** Output stream */
	private final SerialOutputStream out = new SerialOutputStream();
	/** 
	*  get the OutputStream
	*  @return OutputStream
	*/
	public OutputStream getOutputStream()
	{
		if (debug)
			z.reportln( "RXTXPort:getOutputStream() called and returning");
		return out;
	}

	/** Input stream */
	private final SerialInputStream in = new SerialInputStream();
	/** 
	*  get the InputStream
	*  @return InputStream
	*  @see java.io.InputStream
	*/
	public InputStream getInputStream()
	{
		if (debug)
			z.reportln( "RXTXPort:getInputStream() called and returning");
		return in;
	}

	/** 
	*  Set the SerialPort parameters
	*  1.5 stop bits requires 5 databits
	*  @param  b baudrate
	*  @param  d databits
	*  @param  s stopbits
	*  @param  p parity
	*  @throws UnsupportedCommOperationException
	*  @see gnu.io.UnsupportedCommOperationException

	*  If speed is not a predifined speed it is assumed to be
	*  the actual speed desired.
	*/
	private native int nativeGetParity( int fd );
	private native int nativeGetFlowControlMode( int fd );
	public synchronized void setSerialPortParams( int b, int d, int s,
		int p )
		throws UnsupportedCommOperationException
	{
		if (debug)
			z.reportln( "RXTXPort:setSerialPortParams(" +
				b + " " + d + " " + s + " " + p + ") called");
		if ( nativeSetSerialPortParams( b, d, s, p ) )
			throw new UnsupportedCommOperationException(
				"Invalid Parameter" );
		speed = b;
		if( s== STOPBITS_1_5 ) dataBits = DATABITS_5;
		else dataBits = d;
		stopBits = s;
		parity = p;
                if (debug)
			z.reportln( "RXTXPort:setSerialPortParams(" +
				b + " " + d + " " + s + " " + p +
				") returning");
	}

	/**
	*  Set the native serial port parameters
	*  If speed is not a predifined speed it is assumed to be
	*  the actual speed desired.
	*/
	private native boolean nativeSetSerialPortParams( int speed,
		int dataBits, int stopBits, int parity )
		throws UnsupportedCommOperationException;

	/** Line speed in bits-per-second */
	protected int speed=9600;
	/** 
	*  @return  int representing the baudrate
	*  This will not behave as expected with custom speeds
	*/
	public int getBaudRate()
	{
		if (debug)
			z.reportln( "RXTXPort:getBaudRate() called and returning " + speed);
		return speed;
	}

	/** Data bits port parameter */
	protected int dataBits=DATABITS_8;
	/** 
	*  @return int representing the databits
	*/
	public int getDataBits()
	{
		if (debug)
			z.reportln( "RXTXPort:getDataBits() called and returning " + dataBits);
		return dataBits;
	}

	/** Stop bits port parameter */
	protected int stopBits=SerialPort.STOPBITS_1;
	/** 
	*  @return int representing the stopbits
	*/
	public int getStopBits()
	{
		if (debug)
			z.reportln( "RXTXPort:getStopBits() called and returning " + stopBits);
		return stopBits;
	}

	/** Parity port parameter */
	protected int parity= SerialPort.PARITY_NONE;
	/** 
	*  @return int representing the parity
	*/
	public int getParity()
	{
		if (debug)
			z.reportln( "RXTXPort:getParity() called and returning " + parity );
		return parity;
	}


	/** Flow control */
	protected int flowmode = SerialPort.FLOWCONTROL_NONE;
	/** 
	*  @param  flowcontrol FLOWCONTROL_NONE is default
	*  @see gnu.io.SerialPort#FLOWCONTROL_NONE
	*/
	public void setFlowControlMode( int flowcontrol )
	{
		if (debug)
			z.reportln( "RXTXPort:setFlowControlMode( " + flowcontrol + " ) called");
		if(monThreadisInterrupted) 
		{
			if( debug_events )
				z.reportln(  "RXTXPort:setFlowControlMode MonThread is Interrupeted returning" );
			return;
		}
		try {
			setflowcontrol( flowcontrol );
		}
		catch( IOException e )
		{
			e.printStackTrace();
			return;
		}
		flowmode=flowcontrol;
		if (debug)
			z.reportln( "RXTXPort:setFlowControlMode( " + flowcontrol + " ) returning");
	}
	/** 
	*  @return  int representing the flowmode
	*/
	public int getFlowControlMode()
	{
		if (debug)
			z.reportln( "RXTXPort:getFlowControlMode() returning " + flowmode );
		return flowmode;
	}
	native void setflowcontrol( int flowcontrol ) throws IOException;


	/*
	linux/drivers/char/n_hdlc.c? FIXME
		taj@www.linux.org.uk
	*/
	/**
	*  Receive framing control
	*  @param  f framming
	*  @throws UnsupportedCommOperationException
	*/
	public void enableReceiveFraming( int f )
		throws UnsupportedCommOperationException
	{
		if (debug)
			z.reportln( "RXTXPort:enableReceiveFramming() throwing exception");
		throw new UnsupportedCommOperationException( "Not supported" );
	}
	/** 
	*/
	public void disableReceiveFraming()
	{
		if (debug)
			z.reportln( "RXTXPort:disableReceiveFramming() called and returning (noop)");
	}
	/** 
	*  @returns true if framing is enabled
	*/
	public boolean isReceiveFramingEnabled()
	{
		if (debug)
			z.reportln( "RXTXPort:isReceiveFrammingEnabled() called and returning " + false );
		return false;
	}
	/** 
	*  @return  int representing the framing byte
	*/
	public int getReceiveFramingByte()
	{
		if (debug)
			z.reportln( "RXTXPort:getReceiveFrammingByte() called and returning " + 0 );
		return 0;
	}


	/** Receive timeout control */
	protected int timeout;

	/** 
	*  @return  int the timeout
	*/
	public native int NativegetReceiveTimeout();
	/** 
	*  @return  bloolean true if recieve timeout is enabled
	*/
	private native boolean NativeisReceiveTimeoutEnabled();
	/** 
	*  @param  time
	*  @param  threshold
	*  @param  InputBuffer
	*/
	private native void NativeEnableReceiveTimeoutThreshold(int time,
		int threshold,int InputBuffer);
	/** 
	*/
	public void disableReceiveTimeout()
	{
		if (debug)
			z.reportln( "RXTXPort:disableReceiveTimeout() called");
		timeout = -1;
		NativeEnableReceiveTimeoutThreshold( timeout , threshold, InputBuffer );
		if (debug)
			z.reportln( "RXTXPort:disableReceiveTimeout() returning");
	}
	/** 
	*  @param time
	*/
	public void enableReceiveTimeout( int time )
	{
		if (debug)
			z.reportln( "RXTXPort:enableReceiveTimeout() called");
		if( time >= 0 )
		{
			timeout = time;
			NativeEnableReceiveTimeoutThreshold( time , threshold,
				InputBuffer );
		}
		else
		{
			throw new IllegalArgumentException
			(
				"Unexpected negative timeout value"
			);
		}
		if (debug)
			z.reportln( "RXTXPort:enableReceiveTimeout() returning");
	}
	/** 
	*  @return  boolean true if recieve timeout is enabled
	*/
	public boolean isReceiveTimeoutEnabled()
	{
		if (debug)
			z.reportln( "RXTXPort:isReceiveTimeoutEnabled() called and returning " + NativeisReceiveTimeoutEnabled() );
		return( NativeisReceiveTimeoutEnabled() );
	}
	/** 
	*  @return  int the timeout
	*/
	public int getReceiveTimeout()
	{
		if (debug)
			z.reportln( "RXTXPort:getReceiveTimeout() called and returning " + NativegetReceiveTimeout() );
		return(NativegetReceiveTimeout( ));
	}

	/** Receive threshold control */

	private int threshold = 0;

	/** 
	*  @param thresh threshold
	*/
	public void enableReceiveThreshold( int thresh )
	{
		if (debug)
			z.reportln( "RXTXPort:enableReceiveThreshold( " + thresh + " ) called");
		if(thresh >=0)
		{
			threshold=thresh;
			NativeEnableReceiveTimeoutThreshold(timeout, threshold,
				InputBuffer);
		}
		else /* invalid thresh */
		{
			throw new IllegalArgumentException
			(
				"Unexpected negative threshold value"
			);
		}
		if (debug)
			z.reportln( "RXTXPort:enableReceiveThreshold( " + thresh + " ) returned");
	}
	/** 
	*/
	public void disableReceiveThreshold()
	{
		if (debug)
			z.reportln( "RXTXPort:disableReceiveThreshold() called and returning");
		enableReceiveThreshold(0);
	}
	/** 
	*  @return  int the recieve threshold
	*/
	public int getReceiveThreshold()
	{
		if (debug)
			z.reportln( "RXTXPort:getReceiveThreshold() called and returning " + threshold);
		return threshold;
	}
	/** 
	*  @return  boolean true if receive threshold is enabled
	*/
	public boolean isReceiveThresholdEnabled()
	{
		if (debug)
			z.reportln( "RXTXPort:isReceiveThresholdEnable() called and returning" + (threshold > 0) );
		return(threshold>0);
	}

	/** Input/output buffers */
	/** FIXME I think this refers to
		FOPEN(3)/SETBUF(3)/FREAD(3)/FCLOSE(3)
		taj@www.linux.org.uk

		These are native stubs...
	*/
	private int InputBuffer=0;
	private int OutputBuffer=0;
	/** 
	*  @param size
	*/
	public void setInputBufferSize( int size )
	{
		if (debug)
			z.reportln( "RXTXPort:setInputBufferSize( " +
					size + ") called");
		if( size < 0 )
			throw new IllegalArgumentException
			(
				"Unexpected negative buffer size value"
			);
		else InputBuffer=size;
		if (debug)
			z.reportln( "RXTXPort:setInputBufferSize( " +
					size + ") returning");
	}
	/** 
	*/
	public int getInputBufferSize()
	{
		if (debug)
			z.reportln( "RXTXPort:getInputBufferSize() called and returning " + InputBuffer );
		return(InputBuffer);
	}
	/** 
	*  @param size
	*/
	public void setOutputBufferSize( int size )
	{
		if (debug)
			z.reportln( "RXTXPort:setOutputBufferSize( " +
					size + ") called");
		if( size < 0 )
			throw new IllegalArgumentException
			(
				"Unexpected negative buffer size value"
			);
		else OutputBuffer=size;
		if (debug)
			z.reportln( "RXTXPort:setOutputBufferSize( " +
					size + ") returned");
		
	}
	/** 
	*  @return  in the output buffer size
	*/
	public int getOutputBufferSize()
	{
		if (debug)
			z.reportln( "RXTXPort:getOutputBufferSize() called and returning " + OutputBuffer );
		return(OutputBuffer);
	}

	/* =================== cleaned messages to here */

	/**
	*  Line status methods
	*/
	/**
	*  @returns true if DTR is set
	*/
	public native boolean isDTR();
	/** 
	*  @param state
	*/
	public native void setDTR( boolean state );
	/** 
	*  @param state
	*/
	public native void setRTS( boolean state );
	private native void setDSR( boolean state );
	/** 
	*  @return boolean true if CTS is set
	*/
	public native boolean isCTS();
	/** 
	*  @return boolean true if DSR is set
	*/
	public native boolean isDSR();
	/** 
	*  @return boolean true if CD is set
	*/
	public native boolean isCD();
	/** 
	*  @return boolean true if RI is set
	*/
	public native boolean isRI();
	/** 
	*  @return boolean true if RTS is set
	*/
	public native boolean isRTS();


	/**
	*  Write to the port
	*  @param duration
	*/
	public native void sendBreak( int duration );
	protected native void writeByte( int b, boolean i ) throws IOException;
	protected native void writeArray( byte b[], int off, int len, boolean i )
		throws IOException;
	protected native boolean nativeDrain( boolean i ) throws IOException;

	/** RXTXPort read methods */
	protected native int nativeavailable() throws IOException;
	protected native int readByte() throws IOException;
	protected native int readArray( byte b[], int off, int len )
		throws IOException;


	/** Serial Port Event listener */
	private SerialPortEventListener SPEventListener;

	/** Thread to monitor data */
	private MonitorThread monThread;

	/** Process SerialPortEvents */
	native void eventLoop();

	/** 
	*  @return boolean  true if monitor thread is interrupted
	*/
	boolean monThreadisInterrupted=true;
	private native void interruptEventLoop( );
	public boolean checkMonitorThread()
	{
		if (debug)
			z.reportln( "RXTXPort:checkMonitorThread()");
		if(monThread != null)
		{
			if ( debug )
				z.reportln( 
					"monThreadisInterrupted = " +
					monThreadisInterrupted );
			return monThreadisInterrupted;
		}
		if ( debug )
			z.reportln(  "monThread is null " );
		return(true);
	}

	/** 
	*  @param event
	*  @param state
	*  @return boolean true if the port is closing
	*/
	public boolean sendEvent( int event, boolean state )
	{
		if (debug_events)
			z.report( "RXTXPort:sendEvent(");
		/* Let the native side know its time to die */

		if ( fd == 0 || SPEventListener == null || monThread == null)
		{
			return(true);
		}

		switch( event )
		{
			case SerialPortEvent.DATA_AVAILABLE:
				if( debug_events )
					z.reportln( "DATA_AVAILABLE " +
						monThread.Data + ")" );
				break;
			case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
				if( debug_events )
					z.reportln( 
						"OUTPUT_BUFFER_EMPTY " +
						monThread.Output + ")" );
				break;
			case SerialPortEvent.CTS:
				if( debug_events )
					z.reportln( "CTS " +
						monThread.CTS + ")" );
				break;
			case SerialPortEvent.DSR:
				if( debug_events )
					z.reportln( "DSR " +
						monThread.Output + ")" );
				break;
			case SerialPortEvent.RI:
				if( debug_events )
					z.reportln( "RI " +
						monThread.RI + ")" );
				break;
			case SerialPortEvent.CD:
				if( debug_events )
					z.reportln( "CD " +
						monThread.CD + ")" );
				break;
			case SerialPortEvent.OE:
				if( debug_events )
					z.reportln( "OE " +
						monThread.OE + ")" );
				break;
			case SerialPortEvent.PE:
				if( debug_events )
					z.reportln( "PE " +
						monThread.PE + ")" );
				break;
			case SerialPortEvent.FE:
				if( debug_events )
					z.reportln( "FE " +
						monThread.FE + ")" );
				break;
			case SerialPortEvent.BI:
				if( debug_events )
					z.reportln( "BI " +
						monThread.BI + ")" );
				break;
			default:
				if( debug_events )
					z.reportln( "XXXXXXXXXXXXXX " +
						event + ")" );
				break;
		}
		if( debug_events && debug_verbose )
			z.reportln(  "	checking flags " );

		switch( event )
		{
			case SerialPortEvent.DATA_AVAILABLE:
				if( monThread.Data ) break;
				return(false);
			case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
				if( monThread.Output ) break;
				return(false);
			case SerialPortEvent.CTS:
				if( monThread.CTS ) break;
				return(false);
			case SerialPortEvent.DSR:
				if( monThread.DSR ) break;
				return(false);
			case SerialPortEvent.RI:
				if( monThread.RI ) break;
				return(false);
			case SerialPortEvent.CD:
				if( monThread.CD ) break;
				return(false);
			case SerialPortEvent.OE:
				if( monThread.OE ) break;
				return(false);
			case SerialPortEvent.PE:
				if( monThread.PE ) break;
				return(false);
			case SerialPortEvent.FE:
				if( monThread.FE ) break;
				return(false);
			case SerialPortEvent.BI:
				if( monThread.BI ) break;
				return(false);
			default:
				System.err.println( "unknown event: " + event);
				return(false);
		}
		if( debug_events && debug_verbose )
			z.reportln(  "	getting event" );
		SerialPortEvent e = new SerialPortEvent(this, event, !state,
			state );
		if( debug_events && debug_verbose )
			z.reportln(  "	sending event" );
		if(monThreadisInterrupted) 
		{
			if( debug_events )
				z.reportln(  "	sendEvent return" );
			return(true);
		}
		if( SPEventListener != null )
		{
			SPEventListener.serialEvent( e );
		}

		if( debug_events && debug_verbose )
			z.reportln(  "	sendEvent return" );

		if (fd == 0 ||  SPEventListener == null || monThread == null) 
		{
			return(true);
		}
		else 
		{
			return(false);  
		}
	}

	/**
	*  Add an event listener
	*  @param lsnr SerialPortEventListener
	*  @throws TooManyListenersException
	*/

	boolean MonitorThreadLock = true;
	boolean MonitorThreadCloseLock = true;

	public void addEventListener(
		SerialPortEventListener lsnr ) throws TooManyListenersException
	{
		/*  Don't let and notification requests happen until the
		    Eventloop is ready
		*/

		if (debug)
			z.reportln( "RXTXPort:addEventListener()");
		if( SPEventListener != null )
		{
			throw new TooManyListenersException();
		}
		SPEventListener = lsnr;
		if( !MonitorThreadAlive )
		{
			MonitorThreadLock = true;
			monThread = new MonitorThread();
			monThread.start();
			waitForTheNativeCodeSilly();
			MonitorThreadAlive=true;
		}
		if (debug)
			z.reportln( "RXTXPort:Interrupt=false");
	}
	/**
	*  Remove the serial port event listener
	*/
	public void removeEventListener()
	{
		if (debug)
			z.reportln( "RXTXPort:removeEventListener() called");
		waitForTheNativeCodeSilly();
		//if( monThread != null && monThread.isAlive() )
		if( monThreadisInterrupted == true )
		{
			z.reportln( "	RXTXPort:removeEventListener() already interrupted");
			monThread = null;
			SPEventListener = null;
			Runtime.getRuntime().gc();
			return;
		}
		else if( monThread != null && monThread.isAlive() )
		{
			if (debug)
				z.reportln( "	RXTXPort:Interrupt=true");
			monThreadisInterrupted=true;
			/*
			   Notify all threads in this PID that something is up
			   They will call back to see if its their thread
			   using isInterrupted().
			*/
			MonitorThreadCloseLock = true;
			if (debug)
				z.reportln( "	RXTXPort:calling interruptEventLoop");
			interruptEventLoop( );
			if (debug)
				z.reportln("	RXTXPort:waiting on closelock");
			while( MonitorThreadCloseLock )
			{
				if (debug)
					z.reportln("	.");
				try {
					Thread.sleep(100);
				} catch( Exception e ) {}
			}
			if (debug)
				z.reportln("	RXTXPort:set closelock");
			if (debug)
				z.reportln( "	RXTXPort:calling monThread.join()");
			try {
				monThread.join(1000);
			} catch (Exception ex) {
				/* yikes */
				ex.printStackTrace();
			}
			if (debug)
				z.reportln( "	RXTXPort:waiting on isAlive()");
			while( monThread.isAlive() )
			{
				if ( debug )
					z.reportln( "	MonThread is still alive!");
				try {
					monThread.join(1000);
					Thread.sleep( 1000 );
				} catch( Exception e ){} 
				//monThread.stop();
			}
			
		}
		if (debug)
			z.reportln( "	RXTXPort:calling gc()");
		monThread = null;
		SPEventListener = null;
		Runtime.getRuntime().gc();
		MonitorThreadLock = false;
		MonitorThreadAlive=false;
                if (debug)
		  z.reportln( "RXTXPort:removeEventListener() returning");
	}
	/**
	 *	Give the native code a chance to start listening to the hardware
	 *	or should we say give the native code control of the issue.
	 *
	 *	This is important for applications that flicker the Monitor
	 *	thread while keeping the port open.
	 *	In worst case test cases this loops once or twice every time.
	 */

	protected void waitForTheNativeCodeSilly()
	{
		while( MonitorThreadLock )
		{
			try {
				Thread.sleep(100);
			} catch( Exception e ) {}
		}
	}
	/**
	*  @param enable
	*/
	private native void nativeSetEventFlag( int fd, int event,
						boolean flag );
	public void notifyOnDataAvailable( boolean enable )
	{
		if (debug)
			z.reportln( "RXTXPort:notifyOnDataAvailable( " +
				enable+" )");
		
		waitForTheNativeCodeSilly();

		MonitorThreadLock = true;
		nativeSetEventFlag( fd, SerialPortEvent.DATA_AVAILABLE,
					enable );
		monThread.Data = enable;
		MonitorThreadLock = false;
	}

	/**
	*  @param enable
	*/
	public void notifyOnOutputEmpty( boolean enable )
	{
		if (debug)
			z.reportln( "RXTXPort:notifyOnOutputEmpty( " +
				enable+" )");
		waitForTheNativeCodeSilly();
		MonitorThreadLock = true;
		nativeSetEventFlag( fd, SerialPortEvent.OUTPUT_BUFFER_EMPTY,
					enable );
		monThread.Output = enable;
		MonitorThreadLock = false;
	}

	/**
	*  @param enable
	*/
	public void notifyOnCTS( boolean enable )
	{
		if (debug)
			z.reportln( "RXTXPort:notifyOnCTS( " +
				enable+" )");
		waitForTheNativeCodeSilly();
		MonitorThreadLock = true;
		nativeSetEventFlag( fd, SerialPortEvent.CTS, enable );
		monThread.CTS = enable;
		MonitorThreadLock = false;
	}
	/**
	*  @param enable
	*/
	public void notifyOnDSR( boolean enable )
	{
		if (debug)
			z.reportln( "RXTXPort:notifyOnDSR( " +
				enable+" )");
		waitForTheNativeCodeSilly();
		MonitorThreadLock = true;
		nativeSetEventFlag( fd, SerialPortEvent.DSR, enable );
		monThread.DSR = enable;
		MonitorThreadLock = false;
	}
	/**
	*  @param enable
	*/
	public void notifyOnRingIndicator( boolean enable )
	{
		if (debug)
			z.reportln( "RXTXPort:notifyOnRingIndicator( " +
				enable+" )");
		waitForTheNativeCodeSilly();
		MonitorThreadLock = true;
		nativeSetEventFlag( fd, SerialPortEvent.RI, enable );
		monThread.RI = enable;
		MonitorThreadLock = false;
	}
	/**
	*  @param enable
	*/
	public void notifyOnCarrierDetect( boolean enable )
	{
		if (debug)
			z.reportln( "RXTXPort:notifyOnCarrierDetect( " +
				enable+" )");
		waitForTheNativeCodeSilly();
		MonitorThreadLock = true;
		nativeSetEventFlag( fd, SerialPortEvent.CD, enable );
		monThread.CD = enable;
		MonitorThreadLock = false;
	}
	/**
	*  @param enable
	*/
	public void notifyOnOverrunError( boolean enable )
	{
		if (debug)
			z.reportln( "RXTXPort:notifyOnOverrunError( " +
				enable+" )");
		waitForTheNativeCodeSilly();
		MonitorThreadLock = true;
		nativeSetEventFlag( fd, SerialPortEvent.OE, enable );
		monThread.OE = enable;
		MonitorThreadLock = false;
	}
	/**
	*  @param enable
	*/
	public void notifyOnParityError( boolean enable )
	{
		if (debug)
			z.reportln( "RXTXPort:notifyOnParityError( " +
				enable+" )");
		waitForTheNativeCodeSilly();
		MonitorThreadLock = true;
		nativeSetEventFlag( fd, SerialPortEvent.PE, enable );
		monThread.PE = enable;
		MonitorThreadLock = false;
	}
	/**
	*  @param enable
	*/
	public void notifyOnFramingError( boolean enable )
	{
		if (debug)
			z.reportln( "RXTXPort:notifyOnFramingError( " +
				enable+" )");
		waitForTheNativeCodeSilly();
		MonitorThreadLock = true;
		nativeSetEventFlag( fd, SerialPortEvent.FE, enable );
		monThread.FE = enable;
		MonitorThreadLock = false;
	}
	/**
	*  @param enable
	*/
	public void notifyOnBreakInterrupt( boolean enable )
	{
		if (debug)
			z.reportln( "RXTXPort:notifyOnBreakInterrupt( " +
				enable+" )");
		waitForTheNativeCodeSilly();
		MonitorThreadLock = true;
		nativeSetEventFlag( fd, SerialPortEvent.BI, enable );
		monThread.BI = enable;
		MonitorThreadLock = false;
	}

	/** Close the port */
	private native void nativeClose( String name );
	/**
	*/
	boolean closeLock = false;
	public synchronized void close()
	{
		if (debug)
			z.reportln( "RXTXPort:close( " + this.name + " )"); 
		if( closeLock ) return;
		closeLock = true;
		if ( fd <= 0 )
		{
			z.reportln(  "RXTXPort:close detected bad File Descriptor" );
			return;
		}
		setDTR(false);
		setDSR(false);
		if (debug)
			z.reportln( "RXTXPort:close( " + this.name + " ) setting monThreadisInterrupted"); 
		if ( ! monThreadisInterrupted )
		{
			removeEventListener();
		}
		if (debug)
			z.reportln( "RXTXPort:close( " + this.name + " ) calling nativeClose"); 
		nativeClose( this.name );
		if (debug)
			z.reportln( "RXTXPort:close( " + this.name + " ) calling super.close"); 
		super.close();
		if (debug)
			z.reportln( "RXTXPort:close( " + this.name + " ) calling System.gc"); 

		fd = 0;
		Runtime.getRuntime().gc();
		closeLock = false;
		if (debug)
			z.reportln( "RXTXPort:close( " + this.name + " ) leaving"); 
	}


	/** Finalize the port */
	protected void finalize()
	{
		if (debug)
			z.reportln( "RXTXPort:finalize()");
		if( fd > 0 ) close();
		z.finalize();
	}

	/** Inner class for SerialOutputStream */
	class SerialOutputStream extends OutputStream
	{
	/**
	*  @param b
	*  @throws IOException
	*/
		public void write( int b ) throws IOException
		{
			if (debug_write)
				z.reportln( "RXTXPort:SerialOutputStream:write(int)");
			if( speed == 0 ) return;
	/* hmm this turns out to be a very bad idea
			if ( monThreadisInterrupted == true )
			{
				throw new IOException( "Port has been Closed" );
			}
	*/
			waitForTheNativeCodeSilly();
			if ( fd == 0 ) throw new IOException();
			writeByte( b, monThreadisInterrupted );
			if (debug_write)
				z.reportln( "Leaving RXTXPort:SerialOutputStream:write( int )");
		}
	/**
	*  @param b[]
	*  @throws IOException
	*/
		public void write( byte b[] ) throws IOException
		{
			if (debug_write)
			{
				z.reportln( "Entering RXTXPort:SerialOutputStream:write(" + b.length + ") "/* + new String(b)*/ );
			}
			if( speed == 0 ) return;
	/* hmm this turns out to be a very bad idea
			if ( monThreadisInterrupted == true )
			{
				throw new IOException( "Port has been Closed" );
			}
	*/
			if ( fd == 0 ) throw new IOException();
			waitForTheNativeCodeSilly();
			writeArray( b, 0, b.length, monThreadisInterrupted );
			if (debug_write)
				z.reportln( "Leaving RXTXPort:SerialOutputStream:write(" +b.length  +")");
		}
	/**
	*  @param b[]
	*  @param off
	*  @param len
	*  @throws IOException
	*/
		public void write( byte b[], int off, int len )
			throws IOException
		{
			if( speed == 0 ) return;
			if( off + len  > b.length )
			{
				throw new IndexOutOfBoundsException(
					"Invalid offset/length passed to read"
				);
			}
	 
			byte send[] = new byte[len];
			System.arraycopy( b, off, send, 0, len );
			if (debug_write)
			{
				z.reportln( "Entering RXTXPort:SerialOutputStream:write(" + send.length + " " + off + " " + len + " " +") " /*+  new String(send) */ );
			}
			if ( fd == 0 ) throw new IOException();
	/* hmm this turns out to be a very bad idea
			if ( monThreadisInterrupted == true )
			{
				throw new IOException( "Port has been Closed" );
			}
	*/
			waitForTheNativeCodeSilly();
			writeArray( send, 0, len, monThreadisInterrupted );
			if( debug_write )
				z.reportln( "Leaving RXTXPort:SerialOutputStream:write(" + send.length + " " + off + " " + len + " " +") "  /*+ new String(send)*/ );
		}
	/**
	*/
		public void flush() throws IOException
		{
			if (debug)
				z.reportln( "RXTXPort:SerialOutputStream:flush() enter");
			if( speed == 0 ) return;
			if ( fd == 0 ) throw new IOException();
	/* hmm this turns out to be a very bad idea
			if ( monThreadisInterrupted == true )
			{
				return;
				// FIXME Trent this breaks
				//throw new IOException( "flush() Port has been Closed" );
			}
	*/
			waitForTheNativeCodeSilly();
			/* 
			   this is probably good on all OS's but for now
			   just sendEvent from java on Sol
			*/
			if ( nativeDrain( monThreadisInterrupted ) )
				sendEvent( SerialPortEvent.OUTPUT_BUFFER_EMPTY, true );
			if (debug)
				z.reportln( "RXTXPort:SerialOutputStream:flush() leave");
		}
	}

	/** Inner class for SerialInputStream */
	class SerialInputStream extends InputStream
	{
	/**
	*  @return int the int read
	*  @throws IOException
	*  @see java.io.InputStream
*
*timeout threshold       Behavior
*------------------------------------------------------------------------
*0       0       blocks until 1 byte is available timeout > 0,
*                threshold = 0, blocks until timeout occurs, returns -1
*                on timeout
*>0      >0      blocks until timeout, returns - 1 on timeout, magnitude
*                of threshold doesn't play a role.
*0       >0      Blocks until 1 byte, magnitude of  threshold doesn't
*                play a role
	*/
		public synchronized int read() throws IOException
		{
			if (debug_read)
				z.reportln( "RXTXPort:SerialInputStream:read() called");
			if ( fd == 0 ) throw new IOException();
			waitForTheNativeCodeSilly();
			if ( monThreadisInterrupted )
				z.reportln( "+++++++++ read() monThreadisInterrupted" );
			int result = readByte();
			if (debug_read_results)
				z.reportln(  "RXTXPort:SerialInputStrea:read() returns byte = " + result );
			return( result );
		}
	/**
	*  @param b[]
	*  @return int  number of bytes read
	*  @throws IOException
*
*timeout threshold       Behavior
*------------------------------------------------------------------------
*0       0       blocks until 1 byte is available
*>0      0       blocks until timeout occurs, returns 0 on timeout
*>0      >0      blocks until timeout or reads threshold bytes,
                 returns 0 on timeout
*0       >0      blocks until reads threshold bytes
	*/
		public synchronized int read( byte b[] ) throws IOException
		{
			int result;
			if (debug_read)
				z.reportln( "RXTXPort:SerialInputStream:read(" + b.length + ") called");
	/* hmm this turns out to be a very bad idea
			if ( monThreadisInterrupted == true )
			{
				throw new IOException( "Port has been Closed" );
			}
	*/
			waitForTheNativeCodeSilly();
			result = read( b, 0, b.length);
			if (debug_read_results)
				z.reportln(  "RXTXPort:SerialInputStream:read() returned " + result + " bytes" );
			return( result );
		}
/*
read(byte b[], int, int)
Documentation is at http://java.sun.com/products/jdk/1.2/docs/api/java/io/InputStream.html#read(byte[], int, int)
*/
	/**
	*  @param b[]
	*  @param off
	*  @param len
	*  @return int  number of bytes read
	*  @throws IOException
*
*timeout threshold       Behavior
*------------------------------------------------------------------------
*0       0       blocks until 1 byte is available
*>0      0       blocks until timeout occurs, returns 0 on timeout
*>0      >0      blocks until timeout or reads threshold bytes,
                 returns 0 on timeout
*0       >0      blocks until either threshold # of bytes or len bytes,
                 whichever was lower.
	*/
		public synchronized int read( byte b[], int off, int len )
			throws IOException
		{
			if (debug_read)
				z.reportln( "RXTXPort:SerialInputStream:read(" + b.length + " " + off + " " + len + ") called" /*+ new String(b) */ );
			int result;
			/*
			 * Some sanity checks
			 */
			if ( fd == 0 )
			{
				z.reportln("+++++++ IOException()\n");
				throw new IOException();
			}

			if( b==null )
			{
				z.reportln("+++++++ NullPointerException()\n");
				throw new NullPointerException();
			}

			if( (off < 0) || (len < 0) || (off+len > b.length))
			{
				z.reportln("+++++++ IndexOutOfBoundsException()\n");
				throw new IndexOutOfBoundsException();
			}

			/*
			 * Return immediately if len==0
			 */
			if( len==0 ) return 0;

			/*
			 * See how many bytes we should read
			 */
			int Minimum = len;

			if( threshold==0 )
			{
			/*
			 * If threshold is disabled, read should return as soon
			 * as data are available (up to the amount of available
			 * bytes in order to avoid blocking)
			 * Read may return earlier depending of the receive time
			 * out.
			 */
				int a = nativeavailable();
				if( a == 0 )
					Minimum = 1;
				else
					Minimum = Math.min( Minimum, a );
			}
			else
			{
			/*
			 * Threshold is enabled. Read should return when
			 * 'threshold' bytes have been received (or when the
			 * receive timeout expired)
			 */
				Minimum = Math.min(Minimum, threshold);
			}
	/* hmm this turns out to be a very bad idea
			if ( monThreadisInterrupted == true )
			{
				throw new IOException( "Port has been Closed" );
			}
	*/
			waitForTheNativeCodeSilly();
			result = readArray( b, off, Minimum);
			if (debug_read_results)
				z.reportln( "RXTXPort:SerialInputStream:read(" + b.length + " " + off + " " + len + ") returned " + result + " bytes"  /*+ new String(b) */);
			return( result );
		}
	/**
	*  @return int bytes available
	*  @throws IOException
	*/
		public synchronized int available() throws IOException
		{
	/* hmm this turns out to be a very bad idea
			if ( monThreadisInterrupted == true )
			{
				throw new IOException( "Port has been Closed" );
			}
	*/
			if ( debug_verbose )
				z.reportln( "RXTXPort:available() called" );
			int r = nativeavailable();
			if ( debug_verbose )
				z.reportln( "RXTXPort:available() returning " +
					r );
			return r;
		}
	}
	/**
	*/
	class MonitorThread extends Thread
	{
	/** Note: these have to be separate boolean flags because the
	   SerialPortEvent constants are NOT bit-flags, they are just
	   defined as integers from 1 to 10  -DPL */
		private volatile boolean CTS=false;
		private volatile boolean DSR=false;
		private volatile boolean RI=false;
		private volatile boolean CD=false;
		private volatile boolean OE=false;
		private volatile boolean PE=false;
		private volatile boolean FE=false;
		private volatile boolean BI=false;
		private volatile boolean Data=false;
		private volatile boolean Output=false;

		MonitorThread() 
		{
			if (debug)
				z.reportln( "RXTXPort:MontitorThread:MonitorThread()"); 
		}
	/**
	*  run the thread and call the event loop.
	*/
		public void run()
		{
			if (debug)
				z.reportln( "RXTXPort:MontitorThread:run()"); 
			monThreadisInterrupted=false;
			eventLoop();

			if (debug)
				z.reportln( "eventLoop() returned"); 
		}
		protected void finalize() throws Throwable 
		{ 
			if (debug)
				z.reportln( "RXTXPort:MonitorThread exiting"); 
		}
	}
	/**
	*  A dummy method added so RXTX compiles on Kaffee
	*  @deprecated deprecated but used in Kaffe 
	*/
	public void setRcvFifoTrigger(int trigger){};  

/*------------------------  END OF CommAPI -----------------------------*/

	private native static void nativeStaticSetSerialPortParams( String f,
		int b, int d, int s, int p )
		throws UnsupportedCommOperationException;
	private native static boolean nativeStaticSetDSR( String port,
							boolean flag )
		throws UnsupportedCommOperationException;
	private native static boolean nativeStaticSetDTR( String port,
							boolean flag )
		throws UnsupportedCommOperationException;
	private native static boolean nativeStaticSetRTS( String port,
							boolean flag )
		throws UnsupportedCommOperationException;

	private native static boolean nativeStaticIsDSR( String port )
		throws UnsupportedCommOperationException;
	private native static boolean nativeStaticIsDTR( String port )
		throws UnsupportedCommOperationException;
	private native static boolean nativeStaticIsRTS( String port )
		throws UnsupportedCommOperationException;
	private native static boolean nativeStaticIsCTS( String port )
		throws UnsupportedCommOperationException;
	private native static boolean nativeStaticIsCD( String port )
		throws UnsupportedCommOperationException;
	private native static boolean nativeStaticIsRI( String port )
		throws UnsupportedCommOperationException;

	private native static int nativeStaticGetBaudRate( String port )
		throws UnsupportedCommOperationException;
	private native static int nativeStaticGetDataBits( String port )
		throws UnsupportedCommOperationException;
	private native static int nativeStaticGetParity( String port )
		throws UnsupportedCommOperationException;
	private native static int nativeStaticGetStopBits( String port )
		throws UnsupportedCommOperationException;


	private native byte nativeGetParityErrorChar( )
		throws UnsupportedCommOperationException;
	private native boolean nativeSetParityErrorChar( byte b )
		throws UnsupportedCommOperationException;
	private native byte nativeGetEndOfInputChar( )
		throws UnsupportedCommOperationException;
	private native boolean nativeSetEndOfInputChar( byte b )
		throws UnsupportedCommOperationException;
	private native boolean nativeSetUartType(String type, boolean test)
		throws UnsupportedCommOperationException;
	native String nativeGetUartType()
		throws UnsupportedCommOperationException;
	private native boolean nativeSetBaudBase(int BaudBase) 
		throws UnsupportedCommOperationException;
	private native int nativeGetBaudBase() 
		throws UnsupportedCommOperationException;
	private native boolean nativeSetDivisor(int Divisor)
		throws UnsupportedCommOperationException;
	private native int nativeGetDivisor()
		throws UnsupportedCommOperationException;
	private native boolean nativeSetLowLatency()
		throws UnsupportedCommOperationException;
	private native boolean nativeGetLowLatency()
		throws UnsupportedCommOperationException;
	private native boolean nativeSetCallOutHangup(boolean NoHup)
		throws UnsupportedCommOperationException;
	private native boolean nativeGetCallOutHangup()
		throws UnsupportedCommOperationException;

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  This is only accurate up to 38600 baud currently.
	*
	*  @param  port the name of the port thats been preopened
	*  @return BaudRate on success
	*  @throws UnsupportedCommOperationException;
	*  This will not behave as expected with custom speeds
	*
	*/
	public static int staticGetBaudRate( String port )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln( 
				"RXTXPort:staticGetBaudRate( " + port + " )");
		return(nativeStaticGetBaudRate( port ));
	}
	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  @param  port the name of the port thats been preopened
	*  @return DataBits on success
	*  @throws UnsupportedCommOperationException;
	*
	*/
	public static int staticGetDataBits( String port )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln( 
				"RXTXPort:staticGetDataBits( " + port + " )");
		return(nativeStaticGetDataBits( port ) );
	}

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  @param  port the name of the port thats been preopened
	*  @return Parity on success
	*  @throws UnsupportedCommOperationException;
	*
	*/
	public static int staticGetParity( String port )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln( 
				"RXTXPort:staticGetParity( " + port + " )");
		return( nativeStaticGetParity( port ) );
	}

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  @param  port the name of the port thats been preopened
	*  @return StopBits on success
	*  @throws UnsupportedCommOperationException;
	*
	*/
	public static int staticGetStopBits( String port )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln( 
				"RXTXPort:staticGetStopBits( " + port + " )");
			return(nativeStaticGetStopBits( port ) );
	}

	/** 
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  Set the SerialPort parameters
	*  1.5 stop bits requires 5 databits
	*  @param  f filename
	*  @param  b baudrate
	*  @param  d databits
	*  @param  s stopbits
	*  @param  p parity
	*
	*  @throws UnsupportedCommOperationException
	*  @see gnu.io.UnsupportedCommOperationException
	*/

	public static void staticSetSerialPortParams( String f, int b, int d,
		int s, int p )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln( 
				"RXTXPort:staticSetSerialPortParams( " +
				f + " " + b + " " + d + " " + s + " " + p );
		nativeStaticSetSerialPortParams( f, b, d, s, p );
	}

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  Open the port and set DSR.  remove lockfile and do not close
	*  This is so some software can appear to set the DSR before 'opening'
	*  the port a second time later on.
	*
	*  @return true on success
	*  @throws UnsupportedCommOperationException;
	*
	*/

	public static boolean staticSetDSR( String port, boolean flag )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:staticSetDSR( " + port +
						" " + flag );
		return( nativeStaticSetDSR( port, flag ) );
	}

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  Open the port and set DTR.  remove lockfile and do not close
	*  This is so some software can appear to set the DTR before 'opening'
	*  the port a second time later on.
	*
	*  @return true on success
	*  @throws UnsupportedCommOperationException;
	*
	*/

	public static boolean staticSetDTR( String port, boolean flag )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:staticSetDTR( " + port +
						" " + flag );
		return( nativeStaticSetDTR( port, flag ) );
	}

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  Open the port and set RTS.  remove lockfile and do not close
	*  This is so some software can appear to set the RTS before 'opening'
	*  the port a second time later on.
	*
	*  @return none
	*  @throws UnsupportedCommOperationException;
	*
	*/

	public static boolean staticSetRTS( String port, boolean flag )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:staticSetRTS( " + port +
						" " + flag );
		return( nativeStaticSetRTS( port, flag ) );
	}

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  find the fd and return RTS without using a Java open() call
	*
	*  @param String port
	*  @return boolean true if asserted
	*  @throws UnsupportedCommOperationException;
	*
	*/

	public static boolean staticIsRTS( String port )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:staticIsRTS( " + port + " )" );
		return( nativeStaticIsRTS( port ) );
	}
	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  find the fd and return CD without using a Java open() call
	*
	*  @param String port
	*  @return boolean true if asserted
	*  @throws UnsupportedCommOperationException;
	*
	*/

	public static boolean staticIsCD( String port )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln( "RXTXPort:staticIsCD( " + port + " )" );
		return( nativeStaticIsCD( port ) );
	}
	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  find the fd and return CTS without using a Java open() call
	*
	*  @param String port
	*  @return boolean true if asserted
	*  @throws UnsupportedCommOperationException;
	*
	*/

	public static boolean staticIsCTS( String port )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:staticIsCTS( " + port + " )" );
		return( nativeStaticIsCTS( port ) );
	}
	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  find the fd and return DSR without using a Java open() call
	*
	*  @param String port
	*  @return boolean true if asserted
	*  @throws UnsupportedCommOperationException;
	*
	*/

	public static boolean staticIsDSR( String port )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:staticIsDSR( " + port + " )" );
		return( nativeStaticIsDSR( port ) );
	}
	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  find the fd and return DTR without using a Java open() call
	*
	*  @param String port
	*  @return boolean true if asserted
	*  @throws UnsupportedCommOperationException;
	*
	*/

	public static boolean staticIsDTR( String port )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:staticIsDTR( " + port + " )" );
		return( nativeStaticIsDTR( port ) );
	}
	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*
	*  find the fd and return RI without using a Java open() call
	*
	*  @param String port
	*  @return boolean true if asserted
	*  @throws UnsupportedCommOperationException;
	*
	*/

	public static boolean staticIsRI( String port )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:staticIsRI( " + port + " )" );
		return( nativeStaticIsRI( port ) );
	}


	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*  @return int the Parity Error Character
	*  @throws UnsupportedCommOperationException;
	*
	*  Anyone know how to do this in Unix?
	*/

	public byte getParityErrorChar( )
		throws UnsupportedCommOperationException
	{
		byte ret;
		if ( debug )
			z.reportln(  "getParityErrorChar()" );
		ret = nativeGetParityErrorChar();
		if ( debug )
			z.reportln(  "getParityErrorChar() returns " +
						ret );
		return( ret );
	}

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*  @param b Parity Error Character
	*  @return boolean true on success
	*  @throws UnsupportedCommOperationException;
	*
	*  Anyone know how to do this in Unix?
	*/

	public boolean setParityErrorChar( byte b )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "setParityErrorChar(" + b + ")" );
		return( nativeSetParityErrorChar( b ) );
	}

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*  @return int the End of Input Character
	*  @throws UnsupportedCommOperationException;
	*
	*  Anyone know how to do this in Unix?
	*/

	public byte getEndOfInputChar( )
		throws UnsupportedCommOperationException
	{
		byte ret;
		if ( debug )
			z.reportln(  "getEndOfInputChar()" );
		ret = nativeGetEndOfInputChar();
		if ( debug )
			z.reportln(  "getEndOfInputChar() returns " +
						ret );
		return( ret );
	}

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*  @param b End Of Input Character
	*  @return boolean true on success
	*  @throws UnsupportedCommOperationException;
	*/

	public boolean setEndOfInputChar( byte b )
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "setEndOfInputChar(" + b + ")" );
		return( nativeSetEndOfInputChar( b ) );
	}

	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*  @param type String representation of the UART type which mayb
	*  be "none", "8250", "16450", "16550", "16550A", "16650", "16550V2"
	*  or "16750".
	*  @param test boolean flag to determin if the UART should be tested.
	*  @return boolean true on success
	*  @throws UnsupportedCommOperationException;
	*/
	public boolean setUARTType(String type, boolean test)
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:setUARTType()");
		return nativeSetUartType(type, test);
	}
	/**
	*  Extension to CommAPI
	*  This is an extension to CommAPI.  It may not be supported on
	*  all operating systems.
	*  @return type String representation of the UART type which mayb
	*  be "none", "8250", "16450", "16550", "16550A", "16650", "16550V2"
	*  or "16750".
	*  @throws UnsupportedCommOperationException;
	*/
	public String getUARTType() throws UnsupportedCommOperationException
	{
		return nativeGetUartType();
	}

	/**
	*  Extension to CommAPI
	*  @param int BaudBase The clock frequency divided by 16.  Default
	*  BaudBase is 115200.
	*  @return boolean true on success
	*  @throws UnsupportedCommOperationException
	*/

	public boolean setBaudBase(int BaudBase)
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:setBaudBase()");
		return nativeSetBaudBase(BaudBase);
	}

	/**
	*  Extension to CommAPI
	*  @return int BaudBase
	*  @throws UnsupportedCommOperationException
	*/

	public int getBaudBase() throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:getBaudBase()");
		return nativeGetBaudBase();
	}

	/**
	*  Extension to CommAPI
	*  @param int Divisor;
	*  @throws UnsupportedCommOperationException
	*/

	public boolean setDivisor(int Divisor)
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:setDivisor()");
		return nativeSetDivisor(Divisor);
	}

	/**
	*  Extension to CommAPI
	*  @returns int Divisor;
	*  @throws UnsupportedCommOperationException
	*/

	public int getDivisor() throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:getDivisor()");
		return nativeGetDivisor();
	}

	/**
	*  Extension to CommAPI
	*  returns boolean true on success
	*  @throws UnsupportedCommOperationException
	*/

	public boolean setLowLatency() throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:setLowLatency()");
		return nativeSetLowLatency();
	}

	/**
	*  Extension to CommAPI
	*  returns boolean true on success
	*  @throws UnsupportedCommOperationException
	*/

	public boolean getLowLatency() throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:getLowLatency()");
		return nativeGetLowLatency();
	}

	/**
	*  Extension to CommAPI
	*  returns boolean true on success
	*  @throws UnsupportedCommOperationException
	*/

	public boolean setCallOutHangup(boolean NoHup)
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:setCallOutHangup()");
		return nativeSetCallOutHangup(NoHup);
	}

	/**
	*  Extension to CommAPI
	*  returns boolean true on success
	*  @throws UnsupportedCommOperationException
	*/

	public boolean getCallOutHangup()
		throws UnsupportedCommOperationException
	{
		if ( debug )
			z.reportln(  "RXTXPort:getCallOutHangup()");
		return nativeGetCallOutHangup();
	}

/*------------------------  END OF CommAPI Extensions -----------------------*/
}
