 1Content-type: application/x-Unknown; charset=UTF8/**
 * Generic16551.java
 *
 * Generic 16551 Serial Chip Driver
 *
 * ToDo:
 *  - fix the concept of "FIFO-Full" -- this one is messed up
 *
 * History:
 *  11/10/00 Kurt Mahan     Started
 *  07/02/01  pjw           added timeout parameter to new SerialInputStream() call
*/

package com.ajile.drivers.serialport.Generic16551;

import com.ajile.drivers.irq.InterruptController;
import com.ajile.drivers.irq.InterruptEventListener;

import com.ajile.drivers.serialport.aJileCommDriver;
import com.ajile.drivers.serialport.aJileSerialPort;
import com.ajile.drivers.serialport.SerialChip;
import com.ajile.drivers.serialport.ChipMgr;
import com.ajile.drivers.serialport.SerialInputStream;
import com.ajile.drivers.serialport.SerialOutputStream;

import com.ajile.jem.rawJEM;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.Enumeration;
import javax.comm.CommPortIdentifier;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.UnsupportedCommOperationException;

/**
 * Generic 16551 Serial Chip Driver
 *
 * @author Kurt Mahan
 * @version 0.1
 */
public class Generic16551
    extends aJileSerialPort
    implements SerialChip, InterruptEventListener, ChipMgr
{
    /**
     * Register Offsets
     */
    private static final int REG_RBR        = 0; // R  receive buffer reg
    private static final int REG_THR        = 0; // W  transmitter holding reg
    private static final int REG_DLL        = 0; // W  divisor latch LSB
    private static final int REG_DLM        = 1; // W  divisor latch MSB
    private static final int REG_IER        = 1; // W  interrupt enable reg
    private static final int REG_FCR        = 2; // W  FIFO control reg
    private static final int REG_IIR        = 2; // R  int ident reg
    private static final int REG_LCR        = 3; // W  line control reg
    private static final int REG_MCR        = 4; // W  modem control reg
    private static final int REG_LSR        = 5; // R  line status reg
    private static final int REG_MSR        = 6; // R  modem status reg
    private static final int REG_SCR        = 7; // RW scratchpad reg

    /**
     * (IER) Interrupt Enable Register
     */
    private static final int IER_RX_DATA    = 0x01;  // rx data enable
    private static final int IER_TX_EMPTY   = 0x02;  // tx empty enable
    private static final int IER_RX_STATUS  = 0x04;  // rx status enable
    private static final int IER_MOD_STATUS = 0x08;  // modem status enable

    /**
     * (FCR) FIFO Control Register
     */
    private static final int FCR_ENABLE     = 0x01; // fifo enable
    private static final int FCR_RX_RESET   = 0x02; // rx fifo reset
    private static final int FCR_TX_RESET   = 0x04; // tx fifo reset
    private static final int FCR_DMA_MODE   = 0x08; // dma mode select
    private static final int FCR_RX_L_TRIG  = 0x40; // rx lsb trigger
    private static final int FCR_RX_M_TRIG  = 0x80; // rx msb trigger

    /**
     * (IIR) Interrupt Identification Register
     */
    private static final int IIR_PENDING    = 0x01; // interrupt pending
    private static final int IIR_ID_1       = 0x02; // int id bit 1
    private static final int IIR_ID_2       = 0x04; // int id bit 2
    private static final int IIR_ID_3       = 0x08; // int id bit 3
    private static final int IIR_FIFO_EN_1  = 0x40; // fifo enabled (1)
    private static final int IIR_FIFO_EN_2  = 0x80; // fifo enabled (1)

    /**
     * (LCR) Line Control Register
     */
    private static final int LCR_WORD_LEN_0 = 0x01; // word length 0
    private static final int LCR_WORD_LEN_1 = 0x02; // word length 1
    private static final int LCR_STOP_BITS  = 0x04; // stop bits
    private static final int LCR_PARITY     = 0x08; // parity enable
    private static final int LCR_EVEN_PARITY= 0x10; // even parity
    private static final int LCR_STK_PARITY = 0x20; // stick parity
    private static final int LCR_SET_BREAK  = 0x40; // set break
    private static final int LCR_DLAB       = 0x80; // divisor latch access

    /**
     * (MCR) Modem Control Register
     */
    private static final int MCR_DTR        = 0x01; // data terminal ready
    private static final int MCR_RTS        = 0x02; // request to send
    private static final int MCR_EXT_INT    = 0x08; // enable ext int
    private static final int MCR_LOOP       = 0x10; // enable loopback
    private static final int MCR_DEFAULT    = MCR_EXT_INT; // add loopback here

    /**
     * (LSR) Line Status Register
     */
    private static final int LSR_DATA_READY = 0x01; // data ready
    private static final int LSR_OVERRUN    = 0x02; // overrun
    private static final int LSR_PARITY     = 0x04; // parity error
    private static final int LSR_FRAMING    = 0x08; // framing error
    private static final int LSR_BREAK      = 0x10; // break
    private static final int LSR_TX_HLDEMPTY= 0x20; // tx holding reg empty
    private static final int LSR_TX_EMPTY   = 0x40; // tx reg empty
    private static final int LSR_RX_FIFO    = 0x80; // error in rx fifo

    /**
     * (MSR) Modem Status Register
     */
    private static final int MSR_D_CTS      = 0x01; // delta clear to send
    private static final int MSR_D_DSR      = 0x02; // delta data set ready
    private static final int MSR_TE_RING    = 0x04; // trailing edge RI
    private static final int MSR_D_DCD      = 0x08; // delta data carrier det
    private static final int MSR_CTS        = 0x10; // clear to send
    private static final int MSR_DSR        = 0x20; // data set ready
    private static final int MSR_RING       = 0x40; // ring indicator
    private static final int MSR_DCD        = 0x80; // data carrier detect

    /**
     * LCR Constants
     */
    public static final int LCR_DATABITS_5  = 0x00; // 5 data bits
    public static final int LCR_DATABITS_6  = 0x01; // 6 data bits
    public static final int LCR_DATABITS_7  = 0x02; // 7 data bits
    public static final int LCR_DATABITS_8  = 0x03; // 8 data bits

    public static final int LCR_STOPBITS_1  = 0x00; // 1 stop bit 
    public static final int LCR_STOPBITS_15 = 0x04; // 1.5 stop bits(5 databits)
    public static final int LCR_STOPBITS_2  = 0x04; // 2 stop bits

    public static final int LCR_PARITY_ENABLE   = 0x08; // parity enable
    public static final int LCR_PARITY_DISABLE  = 0x00; // parity disable

    public static final int LCR_PARITY_EVEN = 0x10; // even parity
    public static final int LCR_PARITY_ODD  = 0x00; // odd parity

    public static final int LCR_PARITY_STICK = 0x20;

    /**
     * Baud Divisors Table
     */
    public static final int BAUD_110        = 0;
    public static final int BAUD_300        = 1;
    public static final int BAUD_600        = 2;
    public static final int BAUD_1200       = 3;
    public static final int BAUD_2400       = 4;
    public static final int BAUD_4800       = 5;
    public static final int BAUD_9600       = 6;
    public static final int BAUD_19200      = 7;
    public static final int BAUD_38400      = 8;
    public static final int BAUD_57600      = 9;
    public static final int BAUD_115200     = 10;
    public static final int BAUD_MAX_CNT    = 11;

    /*
     *
     * Instance Variables
     *
     */

    /**
     * 16551 Configuration Info
     */
    private int mIER;               // interrupt enable reg flags
    private int mMCR = MCR_DEFAULT; // modem control reg flags

    /**
     * Memory Interface Information
     */
    private int mBaseAddr;

    /**
     * Flow control enabled flag
     */
    private boolean mFlowEnable;

    /**
     * Transmit Disabled flag (for flow control)
     */
    private boolean mTransmitDisable;

    /**
     * Baud Divisors Table
     */
    private int[] mDivisors;

    /**
     * Simple constructor -- the blanks will be filled in/inited later
     */
    public Generic16551()
    {
        super();
        bp16();
    }

    public void bp16() {
        int i = 0;
        i++;
    }

    public void bp17() {
        int i = 0;
        i++;
    }
    /**
     * Constructor
     *
     * @param port IO Port Number (base address)
     * @param memType Not currently used (but there for expansion)
     * @param divTable Divisor table
     */
    public Generic16551(
        int port,
        int memType,
        int[] divTable )
    {
        super();

        bp17();

        // setup the memory
        mBaseAddr = port;

        // setup divisor table
        mDivisors = divTable;

        // disable flow control
        mFlowEnable = false;

        // Disable the transmit disable flag
        mTransmitDisable = false;

        // disable ints
        mIER = 0;
        setReg( REG_IER, mIER );

        // fifo stuff - enable, rx/tx reset, mode 1, 8 byte trigger
        setReg( REG_FCR, FCR_ENABLE |
                         FCR_RX_RESET | FCR_TX_RESET |
                         FCR_DMA_MODE |
                         FCR_RX_M_TRIG );
        
        // modem control reg
        mMCR = MCR_DEFAULT;
        setReg( REG_MCR, mMCR );

        initialize();       // initialize params
    }

    /**
     * Allow the implementing driver to set the MCR bit 7
     *
     * @param enable True if enable bit 7, false if disable
     */
    public void enableMCR7(
        boolean enable )
    {
        mMCR &= 0xffffff7f;
        if ( enable )
            mMCR |= 0x00000080;
        setReg( REG_MCR, mMCR );
        try
        {
            setSerialPortParams( m_baudrate, m_byteSize, m_stopBits, m_parity );
        }
        catch ( Exception e )
        {
        }
    }

    /**
     * Enable the interrupts on the chip
     */
    public void enableIRQ()
    {
        mIER = IER_RX_DATA |
               IER_TX_EMPTY |
               IER_RX_STATUS |
               IER_MOD_STATUS;
        setReg( REG_IER, mIER );

        mMCR |= MCR_EXT_INT;
        setReg( REG_MCR, mMCR );
    }

    /**
     * Setup all the port0s for this chip (Generic only has port0)
     *
     * @param chipKey Prefix of properties that define this chip
     */
    public void initializeChip(
        String chipKey )
    {
        String portString = chipKey + ".port0";
        Enumeration enum = com.ajile.lang.SystemProperties.getProperties().propertyNames();

        int addr = 0;
        String intr = null;
        boolean irqlevel = false;   // default
        int clk = 0;
        int prescaler = 1;          // default
        String portname = null;

        while ( enum.hasMoreElements() ) 
        {
            String propStr = (String)enum.nextElement();
            if ( !propStr.startsWith( portString ) )
                continue;

            String val = System.getProperty( propStr );

            // process name ".name"
            if ( propStr.endsWith( ".name" ) )
            {
                portname = val;
            }

            // process address ".addr"
            else if ( propStr.endsWith( ".addr" ) )
            {
                addr = convertInt( val, 0 );
            }

            // process interrupt ".irq"
            else if ( propStr.endsWith( ".irq" ) )
            {
                intr = val;
            }

            // process irq level ".irqlevel"
            else if ( propStr.endsWith( ".irqlevel" ) )
            {
                if ( val.startsWith( "h" ) || val.startsWith( "H" ) )
                    irqlevel = true;
                else
                    irqlevel = false;
            }

            // process serial clock ".clock"
            else if ( propStr.endsWith( ".clock" ) )
            {
                clk = convertInt( val, 0 );
            }

            // process prescaler ".prescaler"
            else if ( propStr.endsWith( ".prescaler" ) )
            {
                prescaler = convertInt( val, 0 );
            }
        }

        // if we've got enough pieces initialize the chip
        if ( ( addr == 0 ) ||
             ( intr == null ) || ( intr.length() == 0 ) ||
             ( clk == 0 ) ||
             ( portname == null ) || ( portname.length() == 0 ) )
        {
            //System.out.println( "Addr = 0x"+Integer.toHexString( addr ) );
            //System.out.println( "Intr = "+intr );
            //System.out.println( "Clk = "+clk );
            //System.out.println( "PortName = "+portname );
            throw new RuntimeException( "Missing params for "+portString );
        }

        // initialize the chip
        init16551( addr, intr, clk, prescaler, irqlevel );

        // register the chip
        aJileCommDriver commDriver = new aJileCommDriver( this );
        CommPortIdentifier.addPortName( portname,
                                        CommPortIdentifier.PORT_SERIAL,
                                        commDriver );
    }

    /**
     * Convert a String to an integer
     *
     * @param str String
     * @param def Default value
     * @return int Val
     */
    public static int convertInt(
        String str,
        int def )
    {
        try
        {
            if ( str == null )
                return def;

            if ( str.startsWith( "0x" ) || str.startsWith( "0X" ) ) 
            {
                return (int)Long.parseLong( str.substring( 2 ), 16 );
            } 
            return (int)Long.parseLong( str );
        }
        catch ( Exception e )
        {
            throw new RuntimeException( "Invalid Parameter: " + str );
        }
    }

    /**
     * Routine called to initialize the whole chip -- not needed if you use
     * the multiple arg constructor
     *
     * @param base Base address
     * @param intr Interrupt string
     * @param clk Clock rate
     * @param prescaler Prescaler (if any)
     * @param irqlevel TRUE if active HIGH false if active LOW
     */
    public boolean init16551(
        int base,
        String intr,
        int clk,
        int prescaler,
        boolean irqlevel )
    {
        // setup the baudrates
        if ( !computeBaudrates( clk, prescaler ) )
        {
            // unable to compute baudrates
            return false;
        }

        // setup the memory
        mBaseAddr = base;

        // disable ints
        mIER = 0;
        setReg( REG_IER, mIER );

        // fifo stuff - enable, rx/tx reset, mode 1, 8 byte trigger
        setReg( REG_FCR, FCR_ENABLE |
                         FCR_RX_RESET | FCR_TX_RESET |
                         FCR_DMA_MODE |
                         FCR_RX_M_TRIG );
        
        // modem control reg
        mMCR = MCR_DEFAULT;
        setReg( REG_MCR, mMCR );

        initialize();       // initialize params

        // configure the interrupt
        if ( intr == null )
        {
            // no interrupt specified (hope someone else handles it)
            throw new RuntimeException( "Serial 16551: No interrupt specified!" );
        }

        int source = InterruptController.parseInterrupt( intr );
        if ( source == 0 )
        {
            throw new RuntimeException( "Serial 16551: Invalid interrupt specified! - '"+ intr + "'" );
        }
        try
        {
            InterruptController.addInterruptListener( this, source);
            if ( irqlevel )
            {
                InterruptController.setPinInterruptCondition( source,
                                                InterruptController.HIGH_LEVEL);
            }
            else
            {
                // active LOW
                InterruptController.setPinInterruptCondition( source,
                                                InterruptController.LOW_LEVEL);
            }

            // enable the chip IRQs
            enableIRQ();

            InterruptController.enableInterrupt(source);

        }
        catch ( Exception e )
        {
            // error registering interrupt
            throw new RuntimeException( "Serial 16551:  Error registering interrupt" );
        }

        return true;
    }

    /**
     * Compute and fill out baudrate table
     *
     * @param clk Clock rate
     * @param prescaler Prescaler (if any)
     * @return boolean true if success false if failure
     */
    public boolean computeBaudrates(
        int clk,
        int prescaler )
    {
        mDivisors = new int[ BAUD_MAX_CNT ];

        return computeBaudTable( clk, prescaler, mDivisors );
    }

    /**
     * Compute the baud table 
     *
     * @param clk Clock rate
     * @param prescaler Prescaler
     * @param btable Baudrate table to fill in
     * @return boolean true if success false if failure
     */
    public static boolean computeBaudTable(
        int clk,
        int prescaler,
        int[] btable )
    {
        if ( prescaler < 1 ) 
            prescaler = 1;
        if ( clk < 1 )
        {
            throw new RuntimeException("Serial 16551: Invalid CLOCK specified");
        }

        if ( ( btable == null ) || ( btable.length < BAUD_MAX_CNT ) )
        {
            throw new RuntimeException( "Serial 16551: Baud Table Too Short" );
        }

        clk = clk / prescaler;
        btable[ BAUD_110 ] = clk / ( 110 * 16 );
        btable[ BAUD_300 ] = clk / ( 300 * 16 );
        btable[ BAUD_600 ] = clk / ( 600 * 16 );
        btable[ BAUD_1200 ] = clk / ( 1200 * 16 );
        btable[ BAUD_2400 ] = clk / ( 2400 * 16 );
        btable[ BAUD_4800 ] = clk / ( 4800 * 16 );
        btable[ BAUD_9600 ] = clk / ( 9600 * 16 );
        btable[ BAUD_19200 ] = clk / ( 19200 * 16 );
        btable[ BAUD_38400 ] = clk / ( 38400 * 16 );
        btable[ BAUD_57600 ] = clk / ( 57600 * 16 );
        btable[ BAUD_115200 ] = clk / ( 115200 * 16 );

        return true;
    }

    /**
     * Interrupt Handler
     */
    public void interruptEvent()
    {
        while ( true )
        {
            int isr = getReg( REG_IIR );

            // check and see if an interrupt is pending
            if ( ( isr & IIR_PENDING ) != 0 )
                return;

            // process the pending interrupt
            isr = ( isr >> 1 ) & 0x00000007;
            switch ( isr )
            {
                case 0x00:
                    // MSR -- modem status register
                    int msr = getReg( REG_MSR );
                    boolean newCTS = ( ( msr & MSR_CTS ) == 0 );
                    boolean newDSR = ( ( msr & MSR_DSR ) == 0 );
                    boolean newRI = ( ( msr & MSR_RING ) == 0 );
                    boolean newCD = ( ( msr & MSR_DCD ) == 0 );

                    // process changes

                    if ( ( msr & MSR_D_CTS ) != 0 )
                    {
                        // cts change
                        if ( mFlowEnable )
                        {
                            if ( !newCTS && mTransmitDisable )
                            {
                                // enable the transmit interrupt
                                mTransmitDisable = false;
                                mIER = ( mIER | IER_TX_EMPTY );
                                setReg( REG_IER, mIER );
                            }
                            else if ( newCTS && !mTransmitDisable )
                            {
                                // disable the transmit interrupt
                                mTransmitDisable = true;
                                mIER = ( mIER & ~IER_TX_EMPTY );
                                setReg( REG_IER, mIER );
                            }
                        }
                        serialThread.pendSerialEvent( SerialPortEvent.CTS,
                                                       newCTS, m_CTS );
                    }
                    if ( ( msr & MSR_D_DSR ) != 0 )
                        serialThread.pendSerialEvent( SerialPortEvent.DSR,
                                                       newDSR, m_DSR );
                    if ( ( msr & MSR_TE_RING ) != 0 )
                        serialThread.pendSerialEvent( SerialPortEvent.RI,
                                                       newRI, m_RI );
                    if ( ( msr & MSR_D_DCD ) != 0 )
                        serialThread.pendSerialEvent( SerialPortEvent.CD,
                                                       newCD, m_CD );
                    m_CTS = newCTS;
                    m_DSR = newDSR;
                    m_RI = newRI;
                    m_CD = newCD;
                    break;

                case 0x01:
                    // Transmitter hold register empty
                    if ( ( outStream == null ) || 
                         ( !outStream.transmit() ) )
                    {
                        // none or empty output stream -- turn off interrupt
                        mIER = ( mIER & ~IER_TX_EMPTY );
                        setReg( REG_IER, mIER );
                    }
                    break;

                case 0x02:
                    // RX Data Available
                case 0x06:
                    // Character Time Out indicator
                    while ( true )
                    {
                        int lsr = getReg( REG_LSR );
                        if ( ( lsr & LSR_DATA_READY ) == 0 )
                        {

                            serialThread.pendSerialEvent( SerialPortEvent.DATA_AVAILABLE,
                                                       true, true );
                            break;  // fall out (phil had a return here?? )
                        }
                        int data = getReg( REG_RBR );
                        if ( inStream != null )
                            inStream.receive( (byte)data );
                    }
                    break;

                case 0x03:
                    // Receiver Line Status
                    int lsr = getReg( REG_LSR );
                    boolean newOE = ( ( lsr & LSR_OVERRUN ) != 0 );
                    boolean newPE = ( ( lsr & LSR_PARITY ) != 0 );
                    boolean newFE = ( ( lsr & LSR_FRAMING ) != 0 );
                    boolean newBI = ( ( lsr & LSR_BREAK ) != 0 );
                    if ( newOE )
                        serialThread.pendSerialEvent( SerialPortEvent.OE,
                                                       newOE, m_OE );
                    if ( newPE )
                        serialThread.pendSerialEvent( SerialPortEvent.PE,
                                                       newPE, m_PE );
                    if ( newFE )
                        serialThread.pendSerialEvent( SerialPortEvent.FE,
                                                       newFE, m_FE );
                    if ( newBI )
                        serialThread.pendSerialEvent( SerialPortEvent.BI,
                                                       newBI, m_BI );
                    m_OE = newOE;
                    m_PE = newPE;
                    m_FE = newFE;
                    m_BI = newBI;
                    break;

                default:
                    // invalid interrupt state -- *boom*
                    throw new RuntimeException( "Generic16551: Invalid ISR State: "+Integer.toHexString( isr ) );
            }
        }
    }

    /*
     *
     * Serial Chip
     *
     */

    /**
     * Enable the transmit interrupt
     */
    public void enableTXInterrupt()
    {
        // IF flow control don't let the transmitter get enabled
        if ( mFlowEnable && mTransmitDisable )
            return;

        mIER = ( mIER | IER_TX_EMPTY );
        setReg( REG_IER, mIER );
    }

    /**
     * Disable the transmit interrupt
     */
    public void disableTXInterrupt()
    {
        mIER = ( mIER & ~IER_TX_EMPTY );
        setReg( REG_IER, mIER );
    }

    /**
     * Write a byte into the tx queue
     *
     * @param data Byte to write out
     */
    public void writeByte(
        byte data )
    {
        setReg( REG_THR, (int)data );
    }

    /**
     * Check to see if the TX fifo is full
     *
     * @return boolean true if full false if not
     */
    public boolean isTXFifoFull()
    {
        // check if flow control is disabled -- if so this will end
        // the "fill the fifo" routine in SerialOutputStream/transmit()
        if ( mFlowEnable && mTransmitDisable )
            return true;

        return ( ( getReg( REG_LSR ) & LSR_TX_HLDEMPTY ) == 0 );
    }

    /*
     *
     * javax.comm.SerialPort
     *
     */

    /**
     * Set the serial port parameters
     *
     * @param baudrate Baud Rate
     * @param dataBits Data Bits
     * @param stopBits Stop Bits
     * @param parity Parity
     * @throws UnsupportedCommOperationException
     */
    public void setSerialPortParams(
        int baudrate,
        int dataBits,
        int stopBits,
        int parity )
        throws UnsupportedCommOperationException
    {
        int val = 0;
        int lcr = 0;

        // set the baud rate -- a 0 value in mDivisors[] means not supported
        switch ( baudrate )
        {
            case 110: val = mDivisors[ BAUD_110 ]; break;
            case 300: val = mDivisors[ BAUD_300 ]; break;
            case 600: val = mDivisors[ BAUD_600 ]; break;
            case 1200: val = mDivisors[ BAUD_1200 ]; break;
            case 2400: val = mDivisors[ BAUD_2400 ]; break;
            case 4800: val = mDivisors[ BAUD_4800 ]; break;
            case 9600: val = mDivisors[ BAUD_9600 ]; break;
            case 19200: val = mDivisors[ BAUD_19200 ]; break;
            case 38400: val = mDivisors[ BAUD_38400 ]; break;
            case 57600: val = mDivisors[ BAUD_57600 ]; break;
            case 115200: val = mDivisors[ BAUD_115200 ]; break;
            default:
                throw unsupportedCommOperationException;
        }

        if ( val == 0 )
        {
            // invalid divisor -- unknown baud rate
            throw unsupportedCommOperationException;
        }

        m_baudrate = baudrate;
        setReg( REG_MCR, mMCR );        // force update
        lcr = getReg( REG_LCR );
        setReg( REG_LCR, 0x80 );
        setReg( REG_DLL, val & 0x000000ff );
        setReg( REG_DLM, ( val >> 8 ) & 0x000000ff );

        // set the word length
        switch ( dataBits )
        {
            case javax.comm.SerialPort.DATABITS_5: val = 0; break;
            case javax.comm.SerialPort.DATABITS_6: val = 1; break;
            case javax.comm.SerialPort.DATABITS_7: val = 2; break;
            case javax.comm.SerialPort.DATABITS_8: val = 3; break;
            default:
                throw unsupportedCommOperationException;
        }
        m_byteSize = dataBits;
        lcr = ( lcr & 0x000000fc ) | val;

        // set the stop bits
        switch ( stopBits )
        {
            case javax.comm.SerialPort.STOPBITS_1: val = 0; break;
            case javax.comm.SerialPort.STOPBITS_1_5: val = 1; break;
            case javax.comm.SerialPort.STOPBITS_2: val = 1; break;
            default:
                throw unsupportedCommOperationException;
        }
        m_stopBits = stopBits;
        lcr = ( lcr & 0x000000fb ) | ( val << 2 );

        // set the parity
        switch ( parity )
        {
            case javax.comm.SerialPort.PARITY_NONE: val = 0x00; break;
            case javax.comm.SerialPort.PARITY_ODD: val = 0x01; break;
            case javax.comm.SerialPort.PARITY_EVEN: val = 0x03; break;
            case javax.comm.SerialPort.PARITY_MARK: val = 0x05; break;
            case javax.comm.SerialPort.PARITY_SPACE: val = 0x07; break;
            default:
                throw unsupportedCommOperationException;
        }
        m_parity = parity;
        lcr = ( lcr & 0x000000c7 ) | ( val << 3 );

        // update the line control register
        setReg( REG_LCR, lcr );
    }

    /**
     * Set the DTR signal
     *
     * @param dtr State of the DTR signal
     */
    public void setDTR(
        boolean dtr )
    {
        mMCR = ( mMCR & ~MCR_DTR );
        if ( dtr )
            mMCR |= MCR_DTR;
        setReg( REG_MCR, mMCR );
    }

    /**
     * Check the DTR signal
     *
     * @return boolean DTR status
     */
    public boolean isDTR()
    {
        return ( ( getReg( REG_MCR ) & 0x00000001 ) == 1 );
    }

    /**
     * Set the RTS signal
     *
     * @param rts State of the RTS signal
     */
    public void setRTS(
        boolean rts )
    {
        mMCR = ( mMCR & ~MCR_RTS );
        if ( rts )
            mMCR |= MCR_RTS;
        setReg( REG_MCR, mMCR );
    }

    /**
     * Check the RTS signal
     *
     * @return boolean RTS status
     */
    public boolean isRTS()
    {
        return ( ( getReg( REG_MCR ) & 0x00000002 ) == 2 );
    }

    /**
     * Set the FIFO trigger
     *
     * @param trigger Trigger depth
     */
    public void setRcvFifoTrigger(
        int trigger )
    {
    }

    /**
     * Set the flow control mode
     *
     * @param flowcontrol Flow control mode
     * @throws UnsupportedCommOperationException
     */
    public void setFlowControlMode(
        int flowcontrol )
        throws UnsupportedCommOperationException
    {
        switch ( flowcontrol )
        {
            case FLOWCONTROL_NONE:
                // disable flow control
                mFlowEnable = false;
                if ( mTransmitDisable )
                {
                    mTransmitDisable = false;
                    enableTXInterrupt();
                }
                break;

            case FLOWCONTROL_RTSCTS_OUT:
                // throttle xmit based on CTS
                mFlowEnable = true;
                mTransmitDisable = false;
                // check if CTS flag and disable accordingly
                if ( m_CTS )
                {
                    // RACE CONDITION!
                    mTransmitDisable = true;
                    disableTXInterrupt();
                }
                break;

            default:
                // unsupported operation
                throw unsupportedCommOperationException;
        }

        m_flowControl = flowcontrol;
    }

    /*
     *
     * Input/Output Stream Support
     *
     */

    /**
     * Gets an input stream for reading data from the serial port
     *
     * @return InputStream an input stream for reading
     * @throws IOException when the serial port is not opened
     */
    public InputStream getInputStream() 
        throws IOException
    {
        if ( inStream == null )
            inStream = new SerialInputStream( this,inputBufferSize,m_timeOut);

        return inStream;
    }

    /**
     * Gets an output stream for writing data to the serial port
     *
     * @return OutputStream an output stream for writing
     * @throws IOException when the serial port is not opened
     */
    public OutputStream getOutputStream() 
        throws IOException
    {
        if ( outStream == null )
            outStream = new SerialOutputStream( this, outputBufferSize );

        return outStream;
    }

    /*
     *
     * Chip Bus Interface
     *
     */

    /**
     * Set a register on the chip
     *
     * @param reg Register to set
     * @param val Value to set
     */
    private void setReg(
        int reg,
        int val )
    {
        rawJEM.set( mBaseAddr + ( reg << 2 ), val & 0x000000ff );
    }

    /**
     * Get a register on the chip
     *
     * @param reg Register to get
     * @return int Value of register
     */
    private int getReg(
        int reg )
    {
        return rawJEM.getInt( mBaseAddr + ( reg << 2 ) ) & 0x000000ff;
    }
}
