#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#if (defined (NDS_SPECIFIC)) || (defined (NETOS_GNU_TOOLS))
#include <unistd.h>
#endif
#include <ddi.h>
#include "assert.h"
#include "errno.h"

#include "p_registry.h"

#include "netosIo.h"
#include "termios.h"
#include "termios_int.h"
#ifdef NDS_SPECIFIC
#include "SerialSettings.hh"
#ifdef SW_MEI_ENABLED
#include "MeiSettings.hh"
#endif
#ifdef SERIAL_TERM_ENABLED
#include "SerialTermSettings.hh"
#endif

#include "regs.h"
#include "hwtypes.h"
#endif /* NDS_SPECIFIC */

#include "serial_help.h"
#include "nammu.h"

#if NDS_SPECIFIC
#include "hw_strapping.h"
#else
#endif

#include "gpio.h"

//Supposed to be from <fcntl.h> but we can't include that and work
extern "C" int open (const char *filename, int mode, ...);

#define EXIT_SUCCESS	0
#define OK		EXIT_SUCCESS
#define ERROR		(-1)

#ifndef XON
#define XON 0x11
#endif

#ifndef XOFF
#define XOFF 0x13
#endif
#ifdef NDS_SPECIFIC
void
serial_termios_set_defaults(termios *tios)
{
	memset( tios, 0, sizeof(termios) );

	tios->c_iflag = IXON | IXOFF;
	//tios->c_oflag = 0 ;
	tios->c_cflag = CS8 ;
	//tios->c_lflag = 0;
    //tios->c_xflag = 0;
	tios->c_cc[VSTART] = XON;
	tios->c_cc[VSTOP] = XOFF;
	tios->baud = 9600 ;
}

void
match_serl_settings(const SerialSettings& settings, termios &tr)
{
    //Baud
    cfsetospeed(&tr, settings.getBaudRate());

    //Character size
    tr.c_cflag &= ~CSIZE;
    switch(settings.getDataBits()) {
    case 5:
	tr.c_cflag |= CS5;
	break;
    case 6:
	tr.c_cflag |= CS6;
	break;
    case 7:
	tr.c_cflag |= CS7;
	break;
    case 8:
	tr.c_cflag |= CS8;
	break;
    }

    //Stop bits
    switch(settings.getStopBits()) {
    case 1:
	tr.c_cflag &= ~CSTOPB;
	break;
    case 2:
	tr.c_cflag |= CSTOPB;
	break;
    }

    //Parity
    tr.c_cflag &= ~(PARODD | PARENB);
    tr.c_xflag &= ~XPAR;
    switch(settings.getParity()) {
    case PARITY_NONE:
	break;
    case PARITY_ODD:
	tr.c_cflag |= PARENB | PARODD;
	break;
    case PARITY_EVEN:
	tr.c_cflag |= PARENB;
	break;
    case PARITY_MARK:
	tr.c_cflag |= PARENB | PARODD;
	tr.c_xflag |= XPAR;
	break;
    case PARITY_SPACE:
	tr.c_cflag |= PARENB;
	tr.c_xflag |= XPAR;
	break;
    default:
	break;
    }

    //Flow Control
    tr.c_cflag &= ~CMFLOW;
    tr.c_iflag &= ~(IXON | IXOFF | IXANY );
    tr.c_xflag &= ~XTOSS;
    switch(settings.getFlowControl()) {
    case FLOWCONTROL_NONE:
	break;
    case FLOWCONTROL_SOFTWARE:
	tr.c_iflag |= (IXON | IXOFF);
	break;
    case FLOWCONTROL_HARDWARE:
	tr.c_cflag |= CRTSCTS;
	break;
    case FLOWCONTROL_CUSTOM:
	if (settings.getRTSFlow() == RTSFLOW_ON) {
	    tr.c_cflag |= CRTS;
	}
	if (settings.getCTSFlow()) {
	    tr.c_cflag |= CCTS;
	}
	if (settings.getDTRFlow()) {
	    tr.c_cflag |= CDTR;
	}
	if (settings.getDSRFlow()) {
	    tr.c_cflag |= CDSR;
	}
	if (settings.getDCDFlow()) {
	    tr.c_cflag |= CDCD;
	}
	if (settings.getRIFlow()) {
	    tr.c_cflag |= CRI;
	}
	if (settings.getIXOn()) {
	    tr.c_iflag |= IXON;
	}
	if (settings.getIXOff()) {
	    tr.c_iflag |= IXOFF;
	}
	break;
    default:
	break;
    }

    //RTS Flow settings
    if (settings.getRTSFlow() == RTSFLOW_TOGGLE) {
	tr.c_cflag &= ~CRTS;
	tr.c_cflag |= CRTSTOGGLE;
    }

    //RTS delays
    tr.c_cc[VPREDELAY] = settings.getPredelay();
    tr.c_cc[VPOSTDELAY] = settings.getPostdelay();

    //Altpin
    if (settings.isAltPin()) {
	tr.c_xflag |= XALTPIN;
    } else {
	tr.c_xflag &= ~XALTPIN;
    }

#if 0
    if (settings.getDMATimeout()) { // ONLY ON MEDICAL!!
        tr.c_xflag |= XTIMEOUT;
    } else {
        tr.c_xflag &= ~XTIMEOUT;
    }
#endif
    tr.c_cc[VIDLETIME] = settings.getIdletime(); // ONLY ON MEDICAL!!

    tr.c_cc[VBITGAP] = settings.getBitGap();

    // Only on 7520
    if (settings.getXBreak()) {
        tr.c_xflag |= XBREAK;
    } else {
        tr.c_xflag &= ~XBREAK;
    }
}

#ifdef SW_MEI_ENABLED
void
mei_settings_listener(settings_type type, const void * selector)
{
    MeiSettings settings;
    int meiswitches = 0;
    int index = (int)selector;
    int rc;

    assert(type == CN_MEI);
    if (type != CN_MEI) {
#if TRACE
	trace(SERIAL_TRACE, TRACE_CRITICAL,
	      "mei_settings_listener called for non-mei config.");
#endif
	return;
    }

    assert(index >= 0);

    if ((index < 0) || (index >= max_serial_ports())) {
	return;
    }
    settings.load(index);
    if (settings.getMode() == MEI_MODE_232) {
        meiswitches |= DIPSWITCH_232;
    }
    if (settings.getWires() == MEI_WIRES_TWO) {
        meiswitches |= DIPSWITCH_2W;
    }
    if (settings.getTerm()) {
        meiswitches |= DIPSWITCH_TERM;
    }

    rc = SetDipSwitches(index, meiswitches);

    if (rc < 0) {
	assert(0);
#if TRACE
	trace(SERIAL_TRACE, TRACE_WARNING,
	      "Serial configuration request failed.");
#endif
    }
}
#endif // SW_MEI_ENABLED

void
serial_settings_listener(settings_type type, const void * selector)
{
    SerialSettings settings;
    char file[8];
    termios tios;
    int index = (int)selector;
    int rc;

    assert(type == CN_SERIAL);
    if (type != CN_SERIAL) {
#if TRACE
	trace(SERIAL_TRACE, TRACE_CRITICAL,
	      "serial_settings_listener called for non-serial config.");
#endif
	return;
    }

    assert(index >= 0);

    // TODOSOON : For now, only monitor changes to the first serial port
    if ((index < 0) || (index >= max_serial_ports())) {
	return;
    }

    settings.load(index);

    snprintf(file, sizeof(file), "/com/%d", index);

    int fd = open(file, O_CONFIG);
    assert( fd >= 0 );
    if (fd < 0) {
#if TRACE
	trace(SERIAL_TRACE, TRACE_WARNING,
	      "Could not open serial port for configuration.");
#endif
	return;
    }

    tcgetattr(fd, &tios);
    match_serl_settings(settings, tios);
    rc = tcsetattr(fd, TCSANOW, &tios);

    if (rc < 0) {
	assert(0);
#if TRACE
	trace(SERIAL_TRACE, TRACE_WARNING,
	      "Serial configuration request failed.");
#endif
    } else {
	termios tios2;
	tcgetattr(fd, &tios2);
	if (memcmp(&tios, &tios2, sizeof(termios)) != 0) {
	    //assert(0); //These settings should always be allowed
#if TRACE
	    trace(SERIAL_TRACE, TRACE_WARNING,
		  "All required serial config options have not been set.");
#endif
	}
    }

    close(fd);

}
#endif /* NDS_SPECIFIC */

#if NETWORK_STACK == FUSION
extern int NAserialComPortFds[];

extern "C" int serial_fd_to_index(int serial_fd)
{
    int i;
    int max_ports;

    max_ports = max_serial_ports();
    assert(max_ports <= SERIAL_MAX);

    for (i=0; i < max_ports; i++)
    {
        if(NAserialComPortFds[i] == serial_fd)
        {
            return i;
	}
    }

    return -1;
}
#endif
#if NETWORK_STACK == TRECK
extern "C" int serial_fd_to_index(int serial_fd);
#endif

#ifdef NDS_SPECIFIC
extern "C" int serial_restore_termios_settings(int serial_fd)
{
    SerialSettings settings;
    struct termios tios;
    int rc;
    int serial_index;

    if (serial_fd < 0)
    {
        assert(0);
        return -1;
    }

    serial_index = serial_fd_to_index(serial_fd);

    if ((serial_index < 0) || (serial_index >= SERIAL_MAX))
    {
        assert(0);
        return -1;
    }
    
    if (serial_index >= 0 && serial_index < max_serial_ports())
	settings.load(serial_index);
    else
	settings.load(0, CFD_SAVE);

    /* set termios to defaults */
    serial_termios_set_defaults(&tios);

    /* match termios to current serial setting */
    match_serl_settings(settings, tios);

    rc = tcsetattr(serial_fd, TCSANOW, &tios);
    assert(rc >= 0);

#if 0 //eosdebug
    {
        struct termios tios2;

        tcgetattr(serial_fd, &tios2);
        if (memcmp(&tios, &tios2, sizeof(struct termios)) != 0)
        {
            /* these settings should all be allowed */
            assert(0);
        }
    }
#endif /* eosdebug */

    return rc;
}

extern "C" void
serial_init(int index, termios *tp)
{
    SerialSettings settings;

    // An index less than zero can be passed in when the device
    // name is not /com/<x>.  This happens for the /console/<x>
    // devices on AMAT 8/16 for example
    if (index >= 0 && index < max_serial_ports())
	settings.load(index);
    else
	settings.load(0, CFD_SAVE);
	
    match_serl_settings(settings, *tp);
}

void
serial_epoch(void)
{
    Settings::addChangeNotification(CN_SERIAL, &serial_settings_listener);
    #if (PLATFORM != cpd_m)
    

    /*
     *  On a per product basis, configure the serial GPIOs.
     *  If it finds an unexpected hardware type, assert.
     */
    unsigned char hw_type;
    unsigned long fullamask  = 0;
    unsigned long fullavalue = 0;
    unsigned long fullcmask  = 0;
    unsigned long fullcvalue = 0;

    hw_type = GetHardwareType();

    switch(hw_type)
    {
#if PROCESSOR == ns7520
    case DIGI_CONNECT_ME:
    case DIGI_CONNECT_ME_2MB_CLI_ON:
    case DIGI_CONNECT_ME_4:
    case DIGI_CONNECT_ME_4_N2S170:
    case DIGI_CONNECT_ME_KEYCORP:
    case DIGI_CONNECT_ME_4MB_CLI_ON:
    case DIGI_CONNECT_EM:
    case DIGI_CONNECT_WiME:
    case DIGI_CONNECT_WiME_CLI_ON:
    case DIGI_CONNECT_WiEM:
#ifndef GPIOPROG_EXT_ENABLED
	/*
	 *  If not using extended GPIO control elsewhere,
	 *  setup TX and RX for both internal serial ports.
	 */
	fullamask  |= 0x88880000;
	fullavalue |= 0x88800000;
	fullcmask  |= 0x88888800;
	fullcvalue |= 0x88808800;
#endif
#ifndef GPIOPROG_ENABLED
	/*
	 *  If not using GPIO control elsewhere,
	 *  setup the PORTA modem signals as serial.
	 */
	fullamask  |= 0x67670000;
	fullavalue |= 0x67600000;
#endif
	WriteRegMaskData( NARM_PORTA, fullamask, fullavalue );
	WriteRegMaskData( NARM_PORTC, fullcmask, fullcvalue );
	break;
    case DIGI_CONNECT_CPX2:
    case DIGI_CONNECT_WiCPX2:
    case DIGI_CONNECT_CPX2_X:
    case DIGI_CONNECT_WiCPX2_X:
        // The product is manuf. w/ one port.  We are using it for the radio
#ifdef ZIGBEE_LEGACY_SERIAL_ENABLED
        // When the ZIGBEE_LEGACY_SERIAL feature is enabled, the manufactured port
        // will be exposed to allow serial access to the radio - this provides
        // legacy compatibility for Maxstream customers.
#else
        // Report none - the radio is not available for serial access.
        bcomm.bc_ports = 0;
#endif

        // Similar to the ME, but A0 is Zigbee reset
        fullamask |= 0xeeee0000;
        fullavalue |= 0xeee00000;
	fullcmask  |= 0x88888800;
	fullcvalue |= 0x88808800;
	WriteRegMaskData( NARM_PORTA, fullamask, fullavalue );
	WriteRegMaskData( NARM_PORTC, fullcmask, fullcvalue );
        break;
    case DIGI_CONNECT_SP_INT:
        // All serial signals on portA, no serial on C
        WriteRegMaskData( NARM_PORTA, 0xffffff00, 0xffe00000 );
        //WriteRegMaskData( NARM_PORTC, 0, 0); // Managed by other drivers
        break;
    case DIGI_CONNECT_SP:
        case DIGI_CONNECT_SP_RS232:
	WriteRegMaskData( NARM_PORTA, 0x0f0f0f00, 0 ); // All serial special
	//C2 level-high int
	WriteRegMaskData( NARM_PORTC, 0x04040400, 0x04040000 );
	break;
    case DIGI_CONNECT_WiSP:
	WriteRegMaskData( NARM_PORTA, 0x45454500, 0 ); // All serial special
	//C2 level-high int
	WriteRegMaskData( NARM_PORTC, 0x04040400, 0x04040000 );
	break;
    case DIGI_CONNECT_WISP_RS232_2:
    case DIGI_CONNECT_WISP_MEI_2:
        // Dip switch values - all GPIO inputs
	WriteRegMaskData( NARM_PORTA, 0x45454500, 0 );
        // add 88800000 for TXD/RXD - TXD special output, RXD special input
        WriteRegMaskData( NARM_PORTA, 0x88880000, 0x88800000 );
        // A1 422P2EN, GPIO output, start with value high, so inactive
        WriteRegMaskData( NARM_PORTA, 0x02020002, 0x00020002);

	//C2 level-high int
	WriteRegMaskData( NARM_PORTC, 0x04040400, 0x04040000 );

        break;
    case DIGI_CONNECT_SP_RS232_2:
    case DIGI_CONNECT_SP_MEI_2:
        // Dip switch values - all GPIO inputs
	WriteRegMaskData( NARM_PORTA, 0x0f0f0f00, 0 );

	//C2 level-high int
	WriteRegMaskData( NARM_PORTC, 0x04040400, 0x04040000 );
        //C0 422P2EN, GPIO output, start high = inactive
        WriteRegMaskData( NARM_PORTC, 0x01010101, 0x00010001 );
        //C3 and C7, second port TXD and RXD
        WriteRegMaskData( NARM_PORTC, 0x88888800, 0x88808800 );
        break;
    case DIGI_CONNECT_MG:
    case DIGI_CONNECT_WAN:
    case DIGI_CONNECT_RG:
    case DIGI_CONNECT_WAN_S1X:
    case DIGI_CONNECT_RG_S1X:
    case DIGI_CONNECT_WAN_V1X:
    case DIGI_CONNECT_RG_V1X:
    case DIGI_CONNECT_RG_S55:
    case DIGI_CONNECT_WAN_S55:
    case DIGI_CONNECT_RG_S75:
    case DIGI_CONNECT_WAN_S75:
    case DIGI_CONNECT_WAN_S63:
    case DIGI_CONNECT_VPN:
    case DIGI_CONNECT_VPN_S1X:
    case DIGI_CONNECT_VPN_V1X:
    case DIGI_CONNECT_VPN_S55:
    case DIGI_CONNECT_VPN_S75:
    case DIGI_CONNECT_VPN_C1X:
    case DIGI_CONNECT_WAN_C1X:
    case DIGI_CONNECT_RG_C1X:
    case DIGI_CONNECT_SYNC_N12:
    case DIGI_CONNECT_SYNC_S1X:
    case DIGI_CONNECT_SYNC_V1X:
    case DIGI_CONNECT_SYNC_S75:
    case DIGI_CONNECT_WAN_1X:
    case DIGI_CONNECT_VPN_1X:
    case DIGI_CONNECT_RG_1X:
    case DIGI_CONNECT_SYNC_1X:
    case DIGI_CONNECT_WAN_IA_S75:
    case DIGI_CONNECT_WAN_IA_1X:
	// NOTE: If the switch block is removed on the SYNC_16M, then
	// This initial GPIO setup may have to change.
    case DIGI_CONNECT_SYNC_16M_1X:
    case DIGI_CONNECT_SYNC_16M_K300:
    case DIGI_CONNECT_SYNC_16M_S55:
    case DIGI_CONNECT_SYNC_16M_S75:
    case DIGI_CONNECT_SYNC_16M_S63:
	// TXD, RXD, RTS, CTS, DCD, 485 switch, 2w switch
	WriteRegMaskData( NARM_PORTA, 0xefef0000, 0xaba00000 );
	//C2 level-high int, TXD, RXD
	WriteRegMaskData( NARM_PORTC, 0x8c8c8c00, 0x8c848800 );
	break;
    case DIGI_CONNECT_WAN_16M_1X:
    case DIGI_CONNECT_WAN_16M_K300:
    case DIGI_CONNECT_WAN_16M_S55:
    case DIGI_CONNECT_WAN_16M_S75:
    case DIGI_CONNECT_WAN_16M_S63:
    case DIGI_CONNECT_VPN_16M_1X:
    case DIGI_CONNECT_VPN_16M_K300:
    case DIGI_CONNECT_VPN_16M_S55:
    case DIGI_CONNECT_VPN_16M_S75:
    case DIGI_CONNECT_VPN_16M_S63:
    case DIGI_CONNECT_WAN_IA_16M_1X:
    case DIGI_CONNECT_WAN_IA_16M_K300:
    case DIGI_CONNECT_WAN_IA_16M_S55:
    case DIGI_CONNECT_WAN_IA_16M_S75:
    case DIGI_CONNECT_WAN_IA_16M_S63:
	// NOTE: SW selectable MEI signals have already been set up.
	// TXD, RXD, RTS, CTS, DCD, SW selectable MEI
	WriteRegMaskData( NARM_PORTA, 0xabab0000, 0xaba00000 );
	//C2 level-high int, TXD, RXD
	WriteRegMaskData( NARM_PORTC, 0x8c8c8c00, 0x8c848800 );
	break;
#endif /* ns7520 */
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360)
#if PLATFORM != cpx4
    case DIGI_CONNECT_MERCURY:
    case DIGI_CONNECT_TEASER9750:
	/* FIXME: Find out and use the proper approach for these */
    case DIGI_CONNECT_AMSTERDAM:
    case DIGI_CONNECT_CPD:
    case DIGI_CONNECT_4x4:
    case DIGI_CONNECT_AMAT4:
    case DIGI_CONNECT_CPTS4:
    switch (max_serial_ports())
    {
	case 4:
#if PLATFORM != amat4 /* AMAT4 has 3 out of 4 serial ports controlled by ns9360,
              the 4th port being controlled by an external uart */
	    *(volatile unsigned long*)0x9060001c &= ~0x0000ffff;
#ifdef MERC_TIMER_ERRATA_WORKAROUND_ENABLED
            // Enable fn 0, output on RxClk pin - gpio 26
	    *(volatile unsigned long*)0x9060001c |=  0x00004c4c;
#else
	    *(volatile unsigned long*)0x9060001c |=  0x0000444c;
#endif

	    *(volatile unsigned long*)0x90600024 &= ~0xffff0000;
	    *(volatile unsigned long*)0x90600024 |= 0x4c080000;
#endif
	    // Fall through
	case 3:
	    *(volatile unsigned long*)0x90600018 &= ~0xffff0000;
#ifdef MERC_TIMER_ERRATA_WORKAROUND_ENABLED
            // Enable fn 0, output on RxClk pin - gpio 22
	    *(volatile unsigned long*)0x90600018 |=  0x4c4c0000;
#else
	    *(volatile unsigned long*)0x90600018 |=  0x444c0000;
#endif

	    *(volatile unsigned long*)0x90600024 &= ~0x0000ffff;
	    *(volatile unsigned long*)0x90600024 |= 0x00004c08;
	    // Fall through

	case 2:
#if (PLATFORM == cpd)
	    // cpd dosn't support DTR, DSR, RI, or DCD on Channel B However RI is used to output baud baud clock mer merc errata
	    *(volatile unsigned long*)0x90600010 &= ~0xffffffff;
#ifdef MERC_TIMER_ERRATA_WORKAROUND_ENABLED
            // Enable fn 0, output on RxClk pin
	    *(volatile unsigned long*)0x90600010 |= 0x0c004c08;
#else
	    *(volatile unsigned long*)0x90600010 |= 0x00004c08;
#endif // MERC_TIMER_ERRATA_WORKAROUND_ENABLED

#else // PLATFORM
#ifdef MERC_TIMER_ERRATA_WORKAROUND_ENABLED
            // Enable fn 0, output on RxClk pin gpio 6
	    *(volatile unsigned long*)0x90600010 = 0x4c4c4c08;
#else
	    *(volatile unsigned long*)0x90600010 = 0x444c4c08;
#endif // MERC_TIMER_ERRATA_WORKAROUND_ENABLED
#endif // PLATFORM
	    // Fall through

	case 1:
#ifdef MERC_TIMER_ERRATA_WORKAROUND_ENABLED
            // Enable fn 0, output on RxClk pin gpio 14
	    *(volatile unsigned long*)0x90600014 = 0x4c4c4c08;
#else
	    *(volatile unsigned long*)0x90600014 = 0x444c4c08;
#endif
#if (PLATFORM == cpd)
    // CPD gets RI a different way due to the MERC_TIMER_ERRATA_WORKAROUND_ENABLED
    if (GetHardwareStrapping() & DIGI_STRAPPING_CPD_P1 == DIGI_STRAPPING_CPD_P1) {
        // Before Rev C, CPD got RI normally from the UART so we don't need to do anything
    } else {
        // since then, CPD gets RI through GPIO 18 so program that gpio for input
        WriteRegMaskData(NARM_BBUS_UTIL_GCFR03, NARM_BBUS_GCFR03_18_CONFIG_MASK, NARM_BBUS_GCFR03_18_CONFIG_IN);
    }
#endif // (PLATFORM == cpd)

	    break;
	}

	break;
#endif /* PLATFORM != cpx4 */

    case DIGI_CONNECT_CPMESH:
    case DIGI_CONNECT_CPX8_EWCM:
    case DIGI_CONNECT_CPX8_EM:
    case DIGI_CONNECT_CPX8_EWM:
    case DIGI_CONNECT_CPX8_EC:
    case DIGI_CONNECT_CPX8_EWC:
        {
#ifndef MERC_TIMER_ERRATA_WORKAROUND_ENABLED
            //Mercury timer errata always designed into the CPX8 platforms
            insist(0);
#endif
            // All four internal ports get "some" configuration.

            // PORTD is a full serial port
            *(volatile unsigned long*)0x9060001c &= ~0x0000ffff;
            // Enable fn 0, output on RxClk pin - gpio 26
            *(volatile unsigned long*)0x9060001c |=  0x00004c4c;
            
            *(volatile unsigned long*)0x90600024 &= ~0xffff0000;
            *(volatile unsigned long*)0x90600024 |=  0x4c080000;

            // PORTC only uses TX, RX and RXCLK
            *(volatile unsigned long*)0x90600018 &= ~0x0f000000;
            // Enable fn 0, output on RxClk pin - gpio 22
            *(volatile unsigned long*)0x90600018 |=  0x0c000000;
            
            *(volatile unsigned long*)0x90600024 &= ~0x000000ff;
            *(volatile unsigned long*)0x90600024 |=  0x00000008;

            // PORTB only uses TX, RX, RTS, CTS and RXCLK and only
            // when ZIGBEE is enabled.
#ifdef ZIGBEE_ENABLED
            if (RUNTIME_ENABLED(ZIGBEE))
            {
                // Config 0-3,6 as serial function; 4,5,7 as output
#ifdef CPMESHrev02
                // TEMP 2 as input
                // Enable fn 0, output on RxClk pin gpio 6
                *(volatile unsigned long*)0x90600010 = 0xbcbb4308;
#else // CPMESHrev02
                // Enable fn 0, output on RxClk pin gpio 6
                *(volatile unsigned long*)0x90600010 = 0xbcbb4c08;
#endif // CPMESHrev02
                // Set 4,7 to output 1
                *(volatile unsigned long*)0x90600030 |= 0x00000090;
            }
#endif // ZIGBEE_ENABLED

            //PORTA is a full serial port
            // Enable fn 0, output on RxClk pin gpio 14
            *(volatile unsigned long*)0x90600014 = 0x4c4c4c08;
            break;
        }

#if PLATFORM == cpx4
      /* CPX4 has these serial possibilities:
       *  port 0 -> serial console,
       *  port 1 -> unused, (Seagate board with 2 DB9s makes this a serial port) 
       *  port 2 -> Zigbee,
       *  port 3 -> 1-wire
       */
    case DIGI_CONNECT_CPX4_ECM:
    case DIGI_CONNECT_CPX4_ECMV:
    case DIGI_CONNECT_CPX4_EM:
    case DIGI_CONNECT_CPX4_EWM:
    case DIGI_CONNECT_CPX4_NEMA_ECM:
    case DIGI_CONNECT_TS4_EW:
    case DIGI_CONNECT_WAN_3G_EC:
    case DIGI_CONNECT_WAN_3G_ECV:
    case DIGI_CONNECT_WAN_3G_IA_EC:
    case DIGI_CONNECT_WAN_3G_IA_ECV:
    {
#ifndef SW_MEI_ENABLED        
        // CPX4 does not support MEI, but MEI control registers must be set
        // to RS-232 mode.  Do this here because SW_MEI is not enabled.
        *(volatile unsigned char*)0x60000000 = 0x80; /* 232 */
        *(volatile unsigned char*)0x60000001 = 0x80; /* 232 */
        *(volatile unsigned char*)0x60000002 = 0x80; /* 232 */
        *(volatile unsigned char*)0x60000003 = 0x80; /* 232 */
#endif

	/* Port 0: Serial console - 8 signals, RxClk workaround, RI on GPIO36. */
	// Enable fn 0, output on RxClk pin gpio 14
	*(volatile unsigned long*)0x90600014 = 0x4c4c4c08;

        if (max_serial_ports() >1)
        {
            *(volatile unsigned long*)0x90600010 = 0x4c4c4c08;
        }

	/* Port 2: Zigbee - TXD, RXD, RTS, CTS, RxClk workaround. */
	/* Enable Serial port 2 in 232 mode. */

	// Enable fn 0, output on RxClk pin - gpio 22
	/* Set GPIO 22 (RxClk) */
	WriteRegMaskData(NARM_BBUS_UTIL_GCFR03, 0x0F000000, 0x0c000000);

	/* Set GPIO 40-43: TXD, RXD, RTS, CTS */
	WriteRegMaskData(NARM_BBUS_UTIL_GCFR06, 0x0000FFFF, 0x00004c08);

	/* Port 3: 1-wire - TXD, RXD, RxClk workaround. */	
	// Enable fn 0, output on RxClk pin - gpio 26
	/* Set GPIO 26: RxClk */
	WriteRegMaskData(NARM_BBUS_UTIL_GCFR04, 0x0000FFFF, 0x00000c00);

	/* Set GPIO 44-45: TXD, RXD */
	WriteRegMaskData(NARM_BBUS_UTIL_GCFR06, 0x00FF0000, 0x00080000);
	
	break;
    }
    case DIGI_CONNECT_TS1RJ_EW: 
    case DIGI_CONNECT_TS2RJ_EW: 
    case DIGI_CONNECT_TS4RJ_EW: 
#ifndef MERC_TIMER_ERRATA_WORKAROUND_ENABLED
#error "Mercury timer errata always designed into the CPX4 platforms"
#endif
        switch (max_serial_ports()) {
        case 4:
            // Port 3
            *(volatile unsigned long*)0x9060001c &= ~0x0000ffff;
            *(volatile unsigned long*)0x9060001c |=  0x00004c4c;
            *(volatile unsigned long*)0x90600024 &= ~0xffff0000;
            *(volatile unsigned long*)0x90600024 |= 0x4c080000;
            GpioSetInput(GPIO_UARTD_RI);
        case 3:
            // Port 2
            *(volatile unsigned long*)0x90600018 &= ~0xffff0000;
            *(volatile unsigned long*)0x90600018 |=  0x4c4c0000;
            *(volatile unsigned long*)0x90600024 &= ~0x0000ffff;
            *(volatile unsigned long*)0x90600024 |= 0x00004c08;
            GpioSetInput(GPIO_UARTC_RI);
        case 2:
            // Port 1
            *(volatile unsigned long*)0x90600010 = 0x4c4c4c08;
            GpioSetInput(GPIO_UARTB_RI);
        case 1:
            /* Port 0: Serial console - 8 signals, RxClk workaround, */
            *(volatile unsigned long*)0x90600014 = 0x4c4c4c08;
            GpioSetInput(GPIO_UARTA_RI);
        };
        

        // Some may expect to see writes to the MEI control registers
        // here.  However, this will be performed by the
        // mei_settings_listener. 
        break;
#endif

    case DIGI_CONNECT_AMAT8:
    case DIGI_CONNECT_AMAT16:
    case DIGI_CONNECT_AMAT32:
    case DIGI_CONNECT_CPTS8:
    case DIGI_CONNECT_CPTS16:
    case DIGI_CONNECT_CPTS32:
    case DIGI_CONNECT_CPTS8_MEI:
    case DIGI_CONNECT_CPTS16_MEI:
    case DIGI_CONNECT_CPTS32_MEI:
#ifdef MERC_TIMER_ERRATA_WORKAROUND_ENABLED
            // Enable fn 0, output on RxClk pin gpio 14
            /*TODO 4-wires console port !*/

	    *(volatile unsigned long*)0x90600014 = 0x4c4c4c08;
#else
	    *(volatile unsigned long*)0x90600014 = 0x444c4c08;
#endif
	    break;

    case DIGI_CONNECT_ES_4:
    case DIGI_CONNECT_ES_8:
    case DIGI_CONNECT_ES_16:
	// ES uses PCI serial
	break;

#endif /* PROCESSOR == ns9750 */
    default:
	/* Unknown hardware type */
	assert(0);
	break;
    }

#ifdef SW_MEI_ENABLED
    Settings::addChangeNotification(CN_MEI, &mei_settings_listener);

    // Side effect of the call is that initial MEI registration will
    // be performed
    for (int i = 0 ; i < max_serial_ports() ; i++) {
        mei_settings_listener(CN_MEI, (void *)i);
    }
#endif
    

   /* Now we need to set BOOTMUX, once all the serial configuration is
     * done.  If our product doesn't have a BOOTMUX, this has no effect.
     */
    GpioSetOutputWithValue(GPIO_BOOTMUX, 1);
    
    #endif // (PLATFORM != cpd_m)
}

#endif /* NDS_SPECIFIC */

#if NETWORK_STACK == FUSION	    // Needed to keep crtl functions from being pulled in.
/*
 * Function: ttyname()
 *
 * Description: Returns a pointer the the device name of the port
 *
 * Input : Channel number of the port in the system, 0-based
 *
 */
char *ttyname (int desc)
{
    static char ttyName[64];

    if (isatty(desc))
    {
        /*  copy to this scratch pad -- just in case!!  */
        memcpy(ttyName, deviceTable[ desc & 0xff ].name, sizeof(ttyName));
        ttyName[sizeof(ttyName) - 1] = 0;

        return ttyName;
    }

    return NULL;
}
#endif

extern "C" int
tcseta(struct termios *termios_p,
       struct termios *current_tios,
       struct termios *supported_tios)
{
    int status = EIO, i=0;

    //Only one of CRTSTOGGLE and CRTS can be set.  CRTSTOGGLE takes precedence
    if ( (termios_p->c_cflag & CRTSTOGGLE) && (termios_p->c_cflag & CRTS) ) {
	termios_p->c_cflag &= ~CRTS;
    }

    // Clear previous values
    current_tios->c_cflag &= ~supported_tios->c_cflag;
    current_tios->c_xflag &= ~supported_tios->c_xflag;
    current_tios->c_iflag &= ~supported_tios->c_iflag;
    current_tios->c_oflag &= ~supported_tios->c_oflag;

    // Set new values
    current_tios->c_cflag |= termios_p->c_cflag & supported_tios->c_cflag;
    current_tios->c_xflag |= termios_p->c_xflag & supported_tios->c_xflag;
    current_tios->c_iflag |= termios_p->c_iflag & supported_tios->c_iflag;
    current_tios->c_oflag |= termios_p->c_oflag & supported_tios->c_oflag;

    current_tios->baud = termios_p->baud;

    for (i = 0 ; i < NCCS ; i++ ) {
	if (supported_tios->c_cc[i]) {
	    current_tios->c_cc[i] =  termios_p->c_cc[i];
	}
    }

    status = OK;

    return status;
}

/*
 * write_raw_data
 *
 * Write 'srcbuf' data of 'length' to the 'cq' buffer at the 'in' location.
 *
 * sz - should be a power of 2
 * mask - mask of bits for power of 2 of 'sz'. Used for wrap
 */
extern "C" int
write_raw_data(char const *srcbuf, int length,
	       struct circ_queue *cq, int sz, unsigned int mask,
	       int *unused1, struct termios *unused2)
{
    (void)unused1;
    (void)unused2;
    int written = 0;
    while (length) {
	int to_end, to_out, to_write;

	to_end = sz - cq->in;
	to_out = (cq->out - cq->in - 1) & mask;
	to_write = (to_end > to_out) ? to_out : to_end;
	to_write = (length > to_write) ? to_write : length;

	memcpy(&cq->buf[cq->in], srcbuf, to_write);
	cq->in = (cq->in + to_write) & mask;
	srcbuf += to_write;
	written += to_write;
	length -= to_write;

	if ( ((cq->in + 1) & mask) == cq->out) {
	    break;
	}
    }

    return written;
}

/*
 * Much like 'write_raw_data' but will only perform the write if the entire
 * amount will fit into the buffer
 *
 * TODO: We need to figure out how to deal with this function being shared
 * when there is buffer space but insufficient BMDs
 */
static int
write_everything(char const *srcbuf, int length,
	       struct circ_queue *cq, int sz, unsigned int mask)
{
    int to_out = (cq->out - cq->in - 1) & mask;
    if (to_out >= length) {
	write_raw_data(srcbuf, length,
		       cq, sz, mask, 0, 0);
	return 0;
    }
    return 1;
}

#define CT_CTRL 	0
#define CT_BS   	1
#define CT_TAB  	2
#define CT_NL   	3
#define CT_CR		4
#define CT_PRINT	5

char ct_ojump[256] =
{
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_BS,   CT_TAB,  CT_NL,   CT_CTRL,
    CT_CTRL, CT_CR,   CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,

    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,

    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,

    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_CTRL,

    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,
    CT_CTRL, CT_CTRL, CT_CTRL, CT_CTRL,

    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,

    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,

    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT,
    CT_PRINT, CT_PRINT, CT_PRINT, CT_PRINT
};

extern "C" int
write_opost_data(char const *srcbuf, int buflen,
		 struct circ_queue *cq, int sz, unsigned int mask,
		 int *ocol_p, struct termios *tios)
{
    char const *bufstart = srcbuf;
    char const *bufend = srcbuf + buflen;
    int tx_space = (cq->out - cq->in - 1) & mask;
    int ocol = *ocol_p;

    while (srcbuf != bufend && tx_space) {
	char c = *srcbuf;
	int writechar = 1;

	switch (ct_ojump[(int)c]) {
	case CT_BS:
	    if (ocol) ocol--;
	    break;
	case CT_TAB:
	    {
	      //int nspaces = 1 + ((7 - ocol) & 7);
	        int nspaces = 8 - (ocol & 0x7);
		if ( (tios->c_oflag & TABDLY) == TABDLY ) {
		    // Pad w/ spaces to a tabstop of eight
		    if (write_everything("        ", nspaces, cq, sz, mask)
			!= 0)
		    {
			//Unable to write all spaces break the loop
			break;
		    }
		    writechar = 0;
		}
		ocol += nspaces;
	    }
	    break;
	case CT_NL:
	    if (tios->c_oflag & ONLRET) {
		ocol = 0;
	    } else if ( (tios->c_oflag & ONLCR)
			&& ( (ocol != 0)
			     || !(tios->c_oflag & ONOCR) ) )
	    {
		if (write_everything("\r\n", 2, cq, sz, mask) != 0) {
		    //Can't add everything, exit loop
		    break;
		}
		ocol = 0;
		writechar = 0;
	    }
	    break;
	case CT_CR:
	    if ( (tios->c_oflag & ONOCR)
		 && (ocol == 0) )
	    {
		writechar = 0;
	    } else if (tios->c_oflag & OCRNL) {
		writechar = 0;
		if (write_everything("\r\n", 2, cq, sz, mask) != 0) {
		    //Can't add everything, exit loop
		    break;
		}
	    }

	    ocol = 0;
	    break;
	case CT_PRINT:
	    ocol++;
	    break;
	case CT_CTRL:
	    /* Do nothing, not printable, no special actions */
	    break;
	default:
	    // all cases should be handled...
	    assert(0);
	};

	if (writechar) {
	    cq->buf[cq->in] = *srcbuf;
	    cq->in = (cq->in + 1) & mask;
	}

	srcbuf++;
	tx_space = (cq->out - cq->in - 1) & mask;
    }
    *ocol_p = ocol;

    return srcbuf - bufstart;
}

#ifdef NDS_SPECIFIC
extern "C" void
close_logic(int major, int channel)
{
    serial_buffer_t bufs;
    SerialSettings settings;
    int index = serial_channel(major, channel);
    devIoctlFnType myioctl = majorTable[major].ioctl;

    if (index >= 0 && index < max_serial_ports())
	settings.load(index);
    else
	settings.load(0, CFD_SAVE);

    int waittime_start = settings.getCloseWait();

    if (waittime_start == CLOSEWAIT_FOREVER) { // Wait == forever
        myioctl(channel, TCDRAIN, 0);
        return;
    }

    // While there is data in the buffers, and the amount of data does
    // not change after the wait timeout, flush or force drain the
    // buffers and allow the close to complete
    myioctl(channel, TCGETBUFFERS, (char*)&bufs);

    unsigned long int before = bufs.txbuf;
    int waittime = waittime_start;

    while ( waittime  && bufs.txbuf ) {
        tx_thread_sleep(1000);
        myioctl(channel, TCGETBUFFERS, (char*)&bufs);

        if (before == bufs.txbuf) {
            waittime--;
        } else {
            waittime = waittime_start;
            before = bufs.txbuf;
        }
    }

    // Complete close processing - flush or turn off flow control and drain

    if (bufs.txbuf) {

        // flush data
        myioctl(channel, TCFLUSH, (char*)TCOFLUSH);

        // Turn off any flow control and drain
        termios oldtios, newtios;
        myioctl(channel, TCGETA, (char*)&oldtios);
        newtios = oldtios;
        newtios.c_cflag &= ~CMFLOW;
        newtios.c_iflag &= ~( IXON | IXANY | IXOFF | XIXON | XIXON_UNIX );
        myioctl(channel, TCSETA, (char*)&newtios);

        // Wait for any dregs of activity to complete
        myioctl(channel, TCDRAIN, (char*)0);

        // Restore port settings
        myioctl(channel, TCSETA, (char*)&oldtios);
    }

    return;
}
#endif /* NDS_SPECIFIC */

extern "C" uint8 *
MyVaToPhys(const void *virtual_address)
{
    physical_address_t phys;
    int good;
    good = NAVaToPhys(virtual_address, &phys);
    assert(good);
    return (uint8 *) phys;
}

/* For Treck, this function is defined in eos/posix.c */
#if NETWORK_STACK == FUSION
/*
 * Function: isatty()
 *
 * Returns: 1 - if the device is an open serial port, 0 - otherwise
 *
 */
extern "C" int
isatty(int desc)
{
    int idx = desc & ~DDI_BIT;
    deviceInfo *entry = &deviceTable[idx];

    int comport = (strncmp(entry->name, "/com/", 5) == 0);
    int consoleport = (strncmp(entry->name, "/console/", 9) == 0);
    int neoport = (strncmp(entry->name, "/neo/", 5) == 0);

    if (comport || consoleport || neoport) {
	// Test for opened
	int fd = open(entry->name, O_RDWR);
	if (fd == -EBUSY) {
	    return 1;
	}
	if (fd >= 0) {
	    close(fd);
	}
    }
    return 0;
}
#endif

#ifdef NDS_SPECIFIC
#ifdef SERIAL_TERM_ENABLED
// This function helps to enable serial CLI on port 1
extern "C" void
enable_serial_cli (unsigned int serl_index)
{
    SerialTermSettings term_settings;
    Errors errors;

    assert((int)serl_index < max_serial_ports());

    if ((int)serl_index >= max_serial_ports())
        return;
	
    term_settings.load(serl_index, LOCAL_SAVE);

    if (term_settings.getState() == SERIAL_TERM_STATE_DISABLED)
    {
        term_settings.setState(SERIAL_TERM_STATE_ENABLED);

	// This function allows one to override ALL stored settings
	// for "term_settings" based on some external stimulus (like a
	// jumper on MFGI.
        term_settings.save(errors, serl_index, LOCAL_SAVE);
    }
}
#endif

// Given the driver information use devname to find the serial port index
// On error return -1, else return index for event handling and other tasks
extern "C" int
serial_channel(ushort major, ushort minor)
{
    return serial_channel_from_name(devname(major, minor));
}

extern "C" int
serial_channel_from_name(const char *name)
{
    if (name == NULL) {
        return -1;
    }

    // Verify that this is a serial device
    int comport = (strncmp(name, "/com/", 5) == 0);

    if (comport) {
        return atoi(name+5); // Number portion of the name
    }

    return -1;
}

/*
 * Function: max_serial_ports()
 *
 * Return: Returns the number of serial ports available on the sytem
 *
 */
extern "C" int
max_serial_ports(void)
{
    static int num = 0;

#ifdef KYOCERA_M200_PASSTHROUGH_ENABLED
    return 3;
#endif

    if (num == 0) {
	// Count the ports that start with "/com/"
	for (int i = 0 ; i < MAX_NAMES ; i++) {
	    if (strncmp(device_names[i].name, "/com/", 5) == 0) {
		num++;
	    }
	}

	// Bounded by products reported port count
	if (num > bcomm.bc_ports) {
	    num = bcomm.bc_ports;
	}
    }

    return num; /* For now we always have four ports */
}

int dtable_cache[SERIAL_MAX]; // BSS, so default zero
static int
portno_to_dtable(int portno)
{
    // error check portno
    if (portno < 0 || portno >= max_serial_ports()) {
        return -1; // Illegal ports are always open
    }

    if (dtable_cache[portno]) { // Is it in cache already
        return dtable_cache[portno];
    }

    // Find in deviceTable
    char targetName[20];
    sprintf(targetName, "/com/%d", portno);
    int i = 0;
    for (i = 0 ; i < MAX_NAMES ; i++ ) {
        if (strcmp(device_names[i].name, targetName) == 0) {
            break;
        }
    }

    if (i == MAX_NAMES) {
        i = -1; //Store failure in cache
    }

    dtable_cache[portno] = i;
    return i;
}

extern "C" int
serial_isopen(int portno)
{
    int i = portno_to_dtable(portno);
    if (i < 0)
        return 1; // always open

    int ret = 1;
    deviceInfo *major = &majorTable[device_names[i].major];
    major->ioctl(device_names[i].minor, ISOPEN, (char*)&ret);

    return ret;
}

extern "C" int
DipSwitchMode(int portno)
{
    int i = portno_to_dtable(portno);
    if (i < 0)
        return DIPSWITCH_232; // always 232

    int ret = DIPSWITCH_232;
    deviceInfo *major = &majorTable[device_names[i].major];
    major->ioctl(device_names[i].minor, DIPSWITCHMODE, (char*)&ret);

    return ret;
}

#ifdef SW_MEI_ENABLED
extern "C" int
SetDipSwitches(int portno, int switches)
{
    int i = portno_to_dtable(portno);
    if (i < 0)
        return -1;

    deviceInfo *major = &majorTable[device_names[i].major];
    return major->ioctl(device_names[i].minor,
                        SETDIPSWITCHES, (char*)switches);
}
#endif

extern "C" void
patternAlarmExitFlow(int portno)
{
    int i = portno_to_dtable(portno);
    if (i < 0)
        return;

    deviceInfo *major = &majorTable[device_names[i].major];
    major->ioctl(device_names[i].minor, TCFLOW, (char*)TCIAOFF);
}
#else /* NDS_SPECIFIC */
extern "C" int
max_serial_ports(void)
{
    return SERIAL_MAX;
}

extern "C" int
serial_channel(ushort major, ushort minor)
{
    NA_UNUSED_PARAM(major);

    return minor;          
}

extern "C" void
serial_init (int ind, struct termios *tp)
{
    /* need to update this function when we have saved serial settings */
    NA_UNUSED_PARAM(ind);
    NA_UNUSED_PARAM(tp);    
}
#endif /* NDS_SPECIFIC */
