Subversion Repositories Programming Utils

Rev

Blame | Last modification | View Log | RSS feed

package com.rm5248.serial;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * A SerialPort object represents a SerialPort object on the system.  This implementation uses
 * the STREAMS(IO) based method of accessing a serial port.  
 *
 * When creating a SerialPort object, you give the SerialPort the name of the port that you
 * wish to open.
 *
 * When opening a SerialPort and setting values, an {@link IllegalArgumentException} may be thrown
 * if any of the values are NULL.
 *
 * When getting and settings the properties of the SerialPort using one of the {@code getXXX() }
 * or {@code setXXX() } methods, an IllegalStateException will be thrown if the port has been closed.
 *
 * @author rm5248
 *
 */

public class IOSerialPort implements Closeable, SerialPort {

        static{
                System.loadLibrary( "javaserial" );
        }

        private class SerialStateListener implements Runnable{
               
                private volatile boolean stop;
                private SerialChangeListener listen;
               
                SerialStateListener( SerialChangeListener listen ){
                        stop = false;
                        this.listen = listen;
                }

                @Override
                public void run() {
                        while( !stop ){
                                synchronized( serialListenSync ){
                                        try {
                                                serialListenSync.wait();
                                                if( stop ){
                                                        break;
                                                }
                                                listen.serialStateChanged( state );
                                        } catch (Exception e) {}
                                }
                        }
                }
               
                void doStop(){
                        stop = true;
                }
        }

        /* The handle to our internal data structure which keeps track of the port settings.
         * We need a special structure, as on windows we have a HANDLE type, which is void*,
         * yet on Linux we have a file descriptor, which is an int.
         * This is not just a pointer to memory, because if we're running on a 64-bit
         * system, then we might have problems putting it in 32-bits.  Better safe than sorry.
         */

        private int handle;
        /* The input stream that user code uses to read from the serial port. */
        private InputStream inputStream;
        /* The buffered serial input stream which filters out events for us. */
        private BufferedSerialInputStream bis;
        /* The output stream that user code uses to write to the serial port. */
        private SerialOutputStream outputStream;
        /* Make sure we don't close ourselves twice */
        private boolean closed;
        /* The name of the port that's currently open */
        private String portName;
        /* Cache of the last gotten serial line state */
        private volatile SerialLineState state;
        /* Runs in the background to check for serial port events */
        private SerialStateListener serialListen;
        /* Used for synchronizing serialListen */
        private Object serialListenSync;
        /* Depending on if we have a BufferedSerialInputStream or a normal InputStream,
         * this will be set.
         * Note that 'ignoring control lines' really means that we ignore CHANGES on
         * the control lines; it should still be possible to get the state of the lines,
         * you just won't be notified immediately.
         */

        private boolean ignoringControlLines;

        /**
         * Open the specified port, 9600 baud, 8 data bits, 1 stop bit, no parity, no flow control,
         * not ignoring control lines
         *
         * @param portName The name of the port to open
         * @throws NoSuchPortException If this port does not exist
         * @throws NotASerialPortException If the specified port is not a serial port
         */

        public IOSerialPort( String portName ) throws NoSuchPortException, NotASerialPortException {
                this( portName, BaudRate.B9600 );
        }

        /**
         * Open up a serial port, but allow the user to keep the current settings of the serial port.
         *
         * @param portName The port to open
         * @param keepSettings If true, will simply open the serial port without setting anything.  If false, this method
         * acts the same as {@link #SerialPort(String) SerialPort( String portName ) }
         * @param ignoreControlLines If true, all control line changes will be ignored and not propogated
         * back through a {@link SerialChangeListener}.  
         * @throws NoSuchPortException If the port does not exist
         * @throws NotASerialPortException If the port is not in fact a serial port
         */

        public IOSerialPort( String portName, boolean keepSettings, boolean ignoreControlLines ) throws NoSuchPortException, NotASerialPortException{
                if( portName == null ){
                        throw new IllegalArgumentException( "portName must not be null" );
                }
               
                if( keepSettings ){
                        this.handle = openPort( portName );
                        this.portName = portName;
                        this.ignoringControlLines = ignoreControlLines;
                        if( ignoreControlLines ){
                                SimpleSerialInputStream ssis = new SimpleSerialInputStream( handle );
                                inputStream = ssis;
                        }else{
                                SerialInputStream sis = new SerialInputStream( handle );
                                inputStream = sis;
                                bis = new BufferedSerialInputStream( sis, this );
                        }
                        outputStream = new SerialOutputStream( handle );
                        closed = false;

                        SerialLineState s = new SerialLineState();
                        int state = getSerialLineStateInternalNonblocking();
                        // do some sort of bitwise operations here....
                        if( ( state & 0x01 ) > 0 ){
                                s.carrierDetect = true;
                        }
                        if( ( state & (0x01 << 1 ) ) > 0  ){
                                s.clearToSend = true;
                        }
                        if( ( state & (0x01 << 2 ) ) > 0  ){
                                s.dataSetReady = true;
                        }
                        if( ( state & (0x01 << 3 ) ) > 0  ){
                                s.dataTerminalReady = true;
                        }
                        if( ( state & (0x01 << 4 ) ) > 0  ){
                                s.requestToSend = true;
                        }
                        if( ( state & (0x01 << 5 ) ) > 0  ){
                                s.ringIndicator = true;
                        }

                        this.state = s;
                        serialListenSync = new Object();
                }else{
                        this.handle = openPort( portName, 9600, 8, 1, 0, 0 );
                }
               
                if( !ignoreControlLines ){
                        new Thread( bis, "BufferedSerialReader-" + portName ).start();
                }
        }

        /**
         * Open the specified port, no flow control
         *
         * @param portName The name of the port to open
         * @param rate The Baud Rate to open this port at
         * @throws NoSuchPortException If this port does not exist
         * @throws NotASerialPortException If the specified port is not a serial port
         */

        public IOSerialPort( String portName, BaudRate rate )throws NoSuchPortException, NotASerialPortException {
                this( portName, rate, DataBits.DATABITS_8 );
        }

        /**
         * Open the specified port, no flow control
         *
         * @param portName The name of the port to open
         * @param rate The Baud Rate to open this port at
         * @param data The number of data bits
         * @throws NoSuchPortException If this port does not exist
         * @throws NotASerialPortException If the specified port is not a serial port
         */

        public IOSerialPort( String portName, BaudRate rate, DataBits data ) throws NoSuchPortException, NotASerialPortException {
                this( portName, rate, data, StopBits.STOPBITS_1 );
        }

        /**
         * Open the specified port, no parity or flow control
         *
         * @param portName The name of the port to open
         * @param rate The Baud Rate to open this port at
         * @param data The number of data bits
         * @param stop The number of stop bits
         * @throws NoSuchPortException If this port does not exist
         * @throws NotASerialPortException If the specified port is not a serial port
         */

        public IOSerialPort( String portName, BaudRate rate, DataBits data, StopBits stop ) throws NoSuchPortException, NotASerialPortException {
                this( portName, rate, data, stop, Parity.NONE );
        }

        /**
         * Open the specified port, no flow control
         *
         * @param portName The name of the port to open
         * @param rate The Baud Rate to open this port at
         * @param data The number of data bits
         * @param stop The number of stop bits
         * @param parity The parity of the line
         * @throws NoSuchPortException If this port does not exist
         * @throws NotASerialPortException If the specified port is not a serial port
         */

        public IOSerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, Parity parity ) throws NoSuchPortException, NotASerialPortException {
                this( portName, rate, data, stop, parity, FlowControl.NONE );
        }

        /**
         * Open the specified port, defining all options
         *
         * @param portName The name of the port to open
         * @param rate The Buad Rate to open this port at
         * @param data The number of data bits
         * @param stop The number of stop bits
         * @param parity The parity of the line
         * @param flow The flow control of the line
         * @throws NoSuchPortException If this port does not exist
         * @throws NotASerialPortException If the specified port is not a serial port
         */

        public IOSerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, Parity parity, FlowControl flow ) throws NoSuchPortException, NotASerialPortException {
                int myRate = 0;
                int myData = 0;
                int myStop = 0;
                int myParity = 0;
                int myFlow = 0;
                SerialLineState s;
                int state;
                SerialInputStream sis;
               
                //Check for null values in our arguments
                if( portName == null ){
                        throw new IllegalArgumentException( "portName must not be null" );
                }
               
                if( rate == null ){
                        throw new IllegalArgumentException( "rate must not be null" );
                }
               
                if( data == null ){
                        throw new IllegalArgumentException( "data must not be null" );
                }
               
                if( stop == null ){
                        throw new IllegalArgumentException( "stop must not be null" );
                }
               
                if( parity == null ){
                        throw new IllegalArgumentException( "parity must not be null" );
                }
               
                if( flow == null ){
                        throw new IllegalArgumentException( "flow must not be null" );
                }

                //Okay, looks like we're good!
                this.portName = portName;
                closed = false;
                ignoringControlLines = false;

                switch( rate ){
                case B0     :  myRate = 0; break;
                case B50    :  myRate = 50; break;
                case B75    :  myRate = 75; break;
                case B110   :  myRate = 110; break;
                case B134   :  myRate = 134; break;
                case B150   :  myRate = 150; break;
                case B200   :  myRate = 200; break;
                case B300   :  myRate = 300; break;
                case B600   :  myRate = 600; break;
                case B1200  :  myRate = 1200; break;
                case B1800  :  myRate = 1800; break;
                case B2400  :  myRate = 2400; break;
                case B4800  :  myRate = 4800; break;
                case B9600  :  myRate = 9600; break;
                case B38400 :  myRate = 38400; break;
                case B115200:  myRate = 115200; break;
                }        

                switch( data ){
                case DATABITS_5: myData = 5; break;
                case DATABITS_6: myData = 6; break;
                case DATABITS_7: myData = 7; break;
                case DATABITS_8: myData = 8; break;
                }

                switch( stop ){
                case STOPBITS_1: myStop = 1; break;
                case STOPBITS_2: myStop = 2; break;
                }

                switch( parity ){
                case NONE: myParity = 0; break;
                case ODD: myParity = 1; break;
                case EVEN: myParity = 2; break;
                }

                switch( flow ){
                case NONE: myFlow = 0; break;
                case HARDWARE: myFlow = 1; break;
                case SOFTWARE: myFlow = 2; break;
                }

                handle = openPort(portName, myRate, myData, myStop, myParity, myFlow);
                sis = new SerialInputStream( handle );
                inputStream = sis;
                bis = new BufferedSerialInputStream( sis, this );
                outputStream = new SerialOutputStream( handle );

                s = new SerialLineState();
                state = getSerialLineStateInternalNonblocking();
                if( ( state & 0x01 ) > 0 ){
                        s.carrierDetect = true;
                }
                if( ( state & (0x01 << 1 ) ) > 0  ){
                        s.clearToSend = true;
                }
                if( ( state & (0x01 << 2 ) ) > 0  ){
                        s.dataSetReady = true;
                }
                if( ( state & (0x01 << 3 ) ) > 0  ){
                        s.dataTerminalReady = true;
                }
                if( ( state & (0x01 << 4 ) ) > 0  ){
                        s.requestToSend = true;
                }
                if( ( state & (0x01 << 5 ) ) > 0  ){
                        s.ringIndicator = true;
                }

                this.state = s;
                serialListenSync = new Object();
                new Thread( bis, "BufferedSerialReader" ).start();
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#setBaudRate(com.rm5248.serial.SerialPort.BaudRate)
         */

        @Override
        public void setBaudRate( BaudRate rate ){
                int myRate = 0;

                if( closed ){
                        throw new IllegalStateException( "Cannot set the BaudRate once the port has been closed." );
                }
               
                if( rate == null ){
                        throw new IllegalArgumentException( "rate must not be null" );
                }

                switch( rate ){
                case B0     :  myRate = 0; break;
                case B50    :  myRate = 50; break;
                case B75    :  myRate = 75; break;
                case B110   :  myRate = 110; break;
                case B134   :  myRate = 134; break;
                case B150   :  myRate = 150; break;
                case B200   :  myRate = 200; break;
                case B300   :  myRate = 300; break;
                case B600   :  myRate = 600; break;
                case B1200  :  myRate = 1200; break;
                case B1800  :  myRate = 1800; break;
                case B2400  :  myRate = 2400; break;
                case B4800  :  myRate = 4800; break;
                case B9600  :  myRate = 9600; break;
                case B38400 :  myRate = 38400; break;
                case B115200:  myRate = 115200; break;
                }

                setBaudRate( myRate );
        }

        /**
         * Get the input stream used to talk to this device.
         */

        public InputStream getInputStream(){
                if( closed ){
                        throw new IllegalStateException( "Cannot get the input stream once the port has been closed." );
                }

                if( ignoringControlLines ){
                        return inputStream;
                }
               
                return bis;
        }

        /**
         * Get the OutputStream used to talk to this device.
         */

        public OutputStream getOutputStream(){
                if( closed ){
                        throw new IllegalStateException( "Cannot get the output stream once the port has been closed." );
                }

                return outputStream;
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#close()
         */

        @Override
        public void close(){
                if( closed ) return;
                closed = true;
                doClose();
                if( serialListen != null ){
                        serialListen.doStop();
                }
                synchronized( serialListenSync ){
                        serialListenSync.notify();
                }
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#isClosed()
         */

        @Override
        public boolean isClosed(){
                return closed;
        }

        public void finalize(){
                close();
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#setStopBits(com.rm5248.serial.SerialPort.StopBits)
         */

        @Override
        public void setStopBits( StopBits stop ){
                int myStop = 0;

                if( closed ){
                        throw new IllegalStateException( "Cannot set the StopBits once the port has been closed." );
                }
               
                if( stop == null ){
                        throw new IllegalArgumentException( "stop must not be null" );
                }

                switch( stop ){
                case STOPBITS_1: myStop = 1; break;
                case STOPBITS_2: myStop = 2; break;
                }

                setStopBits( myStop );
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#setDataSize(com.rm5248.serial.SerialPort.DataBits)
         */

        @Override
        public void setDataSize( DataBits data ){
                int myData = 0;

                if( closed ){
                        throw new IllegalStateException( "Cannot set the DataBits once the port has been closed." );
                }
               
                if( data == null ){
                        throw new IllegalArgumentException( "data must not be null" );
                }

                switch( data ){
                case DATABITS_5: myData = 5; break;
                case DATABITS_6: myData = 6; break;
                case DATABITS_7: myData = 7; break;
                case DATABITS_8: myData = 8; break;
                }

                setCharSize( myData );
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#setParity(com.rm5248.serial.SerialPort.Parity)
         */

        @Override
        public void setParity( Parity parity ){
                int myParity = 0;

                if( closed ){
                        throw new IllegalStateException( "Cannot set the parity once the port has been closed." );
                }

                if( parity == null ){
                        throw new IllegalArgumentException( "parity must not be null" );
                }
               
                switch( parity ){
                case NONE: myParity = 0; break;
                case ODD: myParity = 1; break;
                case EVEN: myParity = 2; break;
                }

                setParity( myParity );
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#getSerialLineState()
         */

        @Override
        public SerialLineState getSerialLineState() throws IOException{
                if( closed ){
                        throw new IllegalStateException( "Cannot get the serial line state once the port has been closed." );
                }

                int gotState = getSerialLineStateInternalNonblocking();

                SerialLineState s = new SerialLineState();
                // do some sort of bitwise operations here....
                if( ( gotState & 0x01 ) > 0 ){
                        s.carrierDetect = true;
                }
                if( ( gotState & (0x01 << 1 ) ) > 0  ){
                        s.clearToSend = true;
                }
                if( ( gotState & (0x01 << 2 ) ) > 0  ){
                        s.dataSetReady = true;
                }
                if( ( gotState & (0x01 << 3 ) ) > 0  ){
                        s.dataTerminalReady = true;
                }
                if( ( gotState & (0x01 << 4 ) ) > 0  ){
                        s.requestToSend = true;
                }
                if( ( gotState & (0x01 << 5 ) ) > 0  ){
                        s.ringIndicator = true;
                }

                return s;
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#setSerialLineState(com.rm5248.serial.SerialLineState)
         */

        @Override
        public void setSerialLineState( SerialLineState state ){
                if( closed ){
                        throw new IllegalStateException( "Cannot set the serial line state once the port has been closed." );
                }
               
                setSerialLineStateInternal( state );
               
                //Now, because Windows is weird, we need to post a serial changed event here.
                //however, since we can only set DTR and RTS, only post an event if those are
                //the things that changed
                if( this.state.dataTerminalReady != state.dataTerminalReady ||
                                this.state.requestToSend != state.requestToSend ){
                        this.postSerialChangedEvent( state );
                }
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#getBaudRate()
         */

        @Override
        public IOSerialPort.BaudRate getBaudRate(){
                int baudRate;
                BaudRate toReturn;
               
                if( closed ){
                        throw new IllegalStateException( "Cannot get the baud rate once the port has been closed." );
                }
               
                baudRate = getBaudRateInternal();
                toReturn = BaudRate.B0;

                switch( baudRate ){
                case 0     :  toReturn = BaudRate.B0     ; break;
                case 50    :  toReturn = BaudRate.B50    ; break;
                case 75    :  toReturn = BaudRate.B75    ; break;
                case 110   :  toReturn = BaudRate.B110   ; break;
                case 134   :  toReturn = BaudRate.B134   ; break;
                case 150   :  toReturn = BaudRate.B150   ; break;
                case 200   :  toReturn = BaudRate.B200   ; break;
                case 300   :  toReturn = BaudRate.B300   ; break;
                case 600   :  toReturn = BaudRate.B600   ; break;
                case 1200  :  toReturn = BaudRate.B1200  ; break;
                case 1800  :  toReturn = BaudRate.B1800  ; break;
                case 2400  :  toReturn = BaudRate.B2400  ; break;
                case 4800  :  toReturn = BaudRate.B4800  ; break;
                case 9600  :  toReturn = BaudRate.B9600  ; break;
                case 38400 :  toReturn = BaudRate.B38400 ; break;
                case 115200:  toReturn = BaudRate.B115200; break;
                }

                return toReturn;
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#getDataBits()
         */

        @Override
        public IOSerialPort.DataBits getDataBits(){
                int dataBits;
                DataBits bits;
               
                if( closed ){
                        throw new IllegalStateException( "Cannot get the data bits once the port has been closed." );
                }
               
                dataBits = getCharSizeInternal();
                bits = DataBits.DATABITS_8;

                switch( dataBits ){
                case 8: bits = DataBits.DATABITS_8; break;
                case 7: bits = DataBits.DATABITS_7; break;
                case 6: bits = DataBits.DATABITS_6; break;
                case 5: bits = DataBits.DATABITS_5; break;
                }

                return bits;
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#getStopBits()
         */

        @Override
        public IOSerialPort.StopBits getStopBits(){
                int stopBits;
                StopBits bits;
               
                if( closed ){
                        throw new IllegalStateException( "Cannot get stop bits once the port has been closed." );
                }
               
                stopBits = getStopBitsInternal();
                bits = StopBits.STOPBITS_1;

                switch( stopBits ){
                case 1: bits = StopBits.STOPBITS_1; break;
                case 2: bits = StopBits.STOPBITS_2; break;
                }

                return bits;
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#getParity()
         */

        @Override
        public IOSerialPort.Parity getParity(){
                int parity;
                Parity par;
               
                if( closed ){
                        throw new IllegalStateException( "Cannot get the parity once the port has been closed." );
                }
               
                parity = getParityInternal();
                par = Parity.NONE;

                switch( parity ){
                case 0: par = Parity.NONE; break;
                case 1: par = Parity.ODD; break;
                case 2: par = Parity.EVEN; break;
                }

                return par;
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#getFlowControl()
         */

        @Override
        public IOSerialPort.FlowControl getFlowControl(){
                int flowControl;
                FlowControl cont;
               
                if( closed ){
                        throw new IllegalStateException( "Cannot get the flow once the port has been closed." );
                }
               
                flowControl = getFlowControlInternal();
                cont = FlowControl.NONE;

                switch( flowControl ){
                case 0: cont = FlowControl.NONE; break;
                case 1: cont = FlowControl.HARDWARE; break;
                case 2: cont = FlowControl.SOFTWARE; break;
                }

                return cont;
        }
       
        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#setFlowControl(com.rm5248.serial.SerialPort.FlowControl)
         */

        @Override
        public void setFlowControl( FlowControl flow ){
                if( closed ){
                        throw new IllegalStateException( "Cannot set flow once the port has been closed." );
                }
               
                switch( flow ){
                case HARDWARE: setFlowControl( 1 ); break;
                case NONE: setFlowControl( 0 ); break;
                case SOFTWARE: setFlowControl( 2 ); break;
                }
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#setSerialChangeListener(com.rm5248.serial.SerialChangeListener)
         */

        @Override
        public void setSerialChangeListener( SerialChangeListener listen ){
                if( serialListen != null ){
                        serialListen.doStop();
                }
               
                if( listen != null ){
                        serialListen = new SerialStateListener( listen );
                        new Thread( serialListen, "SerialListen" ).start();
                }

        }

        public String toString(){
                return "Serial Port " + portName + ":" + getBaudRate();
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#getPortName()
         */

        @Override
        public String getPortName(){
                return portName;
        }
       
        /**
         * This method is called when the state of the serial lines is changed.
         *
         * @param newState
         */

        void postSerialChangedEvent( SerialLineState newState ){
                if( !state.equals( newState ) ){
                        state = newState;
                       
                        synchronized( serialListenSync ){
                                serialListenSync.notify();
                        }
                }
        }

        /**
         * Open the specified port, return an internal handle to the data structure for this port.
         *
         * @param portName
         * @return
         */

        private native int openPort( String portName, int baudRate, int dataBits, int stopBits, int parity, int flowControl ) throws NoSuchPortException, NotASerialPortException;

        /**
         * Open the specified port, return an internal handle for the data of this port.
         * This method DOES NOT set any of the serial port settings
         *
         * @param portName The port to open
         * @return
         */

        private native int openPort( String portName ) throws NoSuchPortException, NotASerialPortException;

        /**
         * Close this port, release all native resources
         */

        private native void doClose();

        /**
         * Set the baud rate in the native code.
         *
         * @param baudRate
         * @return
         */

        private native boolean setBaudRate( int baudRate );

        private native int getBaudRateInternal();

        /**
         * Set the number of stop bits, once the port has been opened.
         *
         * @param stopBits
         * @return
         */

        private native boolean setStopBits( int stopBits );

        private native int getStopBitsInternal();

        /**
         * Set the character size, once the port has been opened.
         * This should probably be called 'setDataBits'
         *
         * @param charSize
         * @return
         */

        private native boolean setCharSize( int charSize );

        private native int getCharSizeInternal();

        /** Set the parity once the port has been opened.
         *
         * @param parity 0 = None, 1 = Odd, 2 = Even
         * @return
         */

        private native boolean setParity( int parity );

        private native int getParityInternal();

        /** Set the flow control once the port has been opened.
         *
         *
         * @param flowControl 0 = None, 1 = hardware, 2 = software
         * @return
         */

        private native boolean setFlowControl( int flowControl );

        private native int getFlowControlInternal();

        /** Get the serial line state, but don't block when getting it
         *
         * @return
         */

        private native int getSerialLineStateInternalNonblocking();
       
        /**
         * Set the state of the serial line.
         * @return
         */

        private native int setSerialLineStateInternal( SerialLineState s );
       
        //
        // Static Methods
        //
       
        /**
         * Get the major version of this library.  For example, if this is version
         * 0.2, this returns 0
         */

        public static int getMajorVersion(){
                return 0;
        }
       
        /**
         * Get the minor version of this library.  For example, if this is version
         * 0.2, this returns 2.
         */

        public static int getMinorVersion(){
                return 4;
        }
       
        /**
         * Get the major version of the native code.  This should match up with
         * {@link #getMajorVersion() getMajorVersion()}, although this is not
         * guaranteed.  For example, if this is version 0.2, this returns 0
         */

        public static native int getMajorNativeVersion();
       
        /**
         * Get the minor version of the native code.  This should match up with
         * {@link #getMinorVersion() getMinorVersion()}, although this is not
         * guaranteed.  For example, if this is version 0.2, this returns 2.
         */

        public static native int getMinorNativeVersion();
       
        /**
         * <p>
         * Get an array of all the serial ports on the system.  For example, on
         * Windows this will return {@code { "COM1", "COM3", .... } } depending on how
         * many serial devices you have plugged in.  On Linux, this returns
         * {@code { "/dev/ttyS0", "/dev/ttyUSB0", "/dev/symlink", ... } }
         * It will not resolve symlinks, such that if there is a symlink
         * from {@code /dev/symlink } to {@code /dev/ttyUSB0 }, they will both show up.
         * </p>
         * <p>
         * <b>NOTE:</b> this will only return ports that you have permissions to
         * open.
         * </p>
         *
         * @return
         */

        public static native String[] getSerialPorts();

}