/*****************************************************************************/
/*
 *           pclint.c  -- ADVANTECH Industrial Communication Card
 *	 		  serail driver
 */

/*****************************************************************************/
/*	      
 *         moxa.c -- MOXA Intellio family multiport serial driver.
 *
 *      Copyright (C) 1999-2000  Moxa Technologies (support@moxa.com.tw).
 *
 *      This code is loosely based on the Linux serial driver, written by
 *      Linus Torvalds, Theodore T'so and others.
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 *	MOXA Intellio Series Driver
 *	for		: LINUX
 *	date		: 1999/12/8
 *	version 	: 5.1
 */

#ifdef MODULE
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>
#else
#define	MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#endif
#include <linux/types.h>
#include <linux/autoconf.h>
#include <linux/version.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/config.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/serial.h>
#include <linux/tty_driver.h>
#include <linux/delay.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/bitops.h>

#define		MOXA_VERSION		"1.0"
#define		VERSION_CODE(ver,rel,seq)	((ver << 16) | (rel << 8) | seq)

#define MOXAMAJOR       31 
#define MOXACUMAJOR     37 

#ifdef CONFIG_PCI
#if (LINUX_VERSION_CODE < VERSION_CODE(2,1,0))
#include <linux/bios32.h>
#endif
#include <linux/pci.h>
#endif /* ENABLE_PCI */

#if (LINUX_VERSION_CODE < VERSION_CODE(2,1,0))

#define copy_from_user memcpy_fromfs
#define copy_to_user memcpy_tofs
#define put_to_user(arg1, arg2) put_fs_long(arg1, (unsigned long *)arg2)
#define get_from_user(arg1, arg2) arg1 = get_fs_long((unsigned long *)arg2)
#define ioremap vremap
#define iounmap vfree
#define schedule_timeout(x) {current->timeout = jiffies + (x); schedule();}
#define signal_pending(x) ((x)->signal & ~(x)->blocked)
#else
#include <asm/uaccess.h>
#define put_to_user(arg1, arg2) put_user(arg1, (unsigned long *)arg2)
#define get_from_user(arg1, arg2) get_user(arg1, (unsigned int *)arg2)
#endif 


typedef unsigned char	uchar;
typedef unsigned long * f_ulong;
typedef uchar * 	f_uchar;
typedef char *		f_char;
typedef ushort *	f_ushort;
typedef short * 	f_short;

#define MAX_BOARDS 		4	/* Don't change this value */
#define MAX_PORTS_PER_BOARD	32	/* Don't change this value */
#define MAX_PORTS 		128	/* Don't change this value */

/*
 *	Define the Moxa PCI vendor and device IDs.
 */
#define MOXA_BUS_TYPE_ISA		0
#define MOXA_BUS_TYPE_PCI		1

#ifndef	PCI_VENDOR_ID_MOXA
#define	PCI_VENDOR_ID_MOXA	0x1393
#endif
#ifndef PCI_DEVICE_ID_CP204J
#define PCI_DEVICE_ID_CP204J	0x2040
#endif
#ifndef PCI_DEVICE_ID_C218
#define PCI_DEVICE_ID_C218	0x2180
#endif
#ifndef PCI_DEVICE_ID_C320
#define PCI_DEVICE_ID_C320	0x3200
#endif

enum	{
	MOXA_BOARD_C218_PCI = 1,
	MOXA_BOARD_C218_ISA,
	MOXA_BOARD_C320_PCI,
	MOXA_BOARD_C320_ISA,
	MOXA_BOARD_CP204J,
};

static char *moxa_brdname[] = {
	"C218 Turbo PCI series",
	"PCL-844+  ISA series",
	"C320 Turbo PCI series",
	"PCL-747+   ISA series",
	"CP-204J series",
};

#ifdef CONFIG_PCI

typedef struct {
	unsigned short	vendor_id;
	unsigned short	device_id;
	unsigned short	board_type;
}	moxa_pciinfo;

static moxa_pciinfo	moxa_pcibrds[] = {
	{PCI_VENDOR_ID_MOXA,PCI_DEVICE_ID_C218 ,MOXA_BOARD_C218_PCI},
	{PCI_VENDOR_ID_MOXA,PCI_DEVICE_ID_C320 ,MOXA_BOARD_C320_PCI},
	{PCI_VENDOR_ID_MOXA,PCI_DEVICE_ID_CP204J ,MOXA_BOARD_CP204J},
};

#endif

typedef struct _moxa_isa_board_conf {
	int	boardType;
	int	numPorts;
	ulong	baseAddr;
} moxa_isa_board_conf;

static moxa_isa_board_conf moxa_isa_boards[] =  {
	{0,0,0},
	{0,0,0},
	{0,0,0},
	{0,0,0}
/*	 {MOXA_BOARD_C218_ISA,8,0xDC000}, */
};

typedef struct	_moxa_pci_devinfo {
	ushort	busNum;
	ushort	devNum;
} moxa_pci_devinfo;

typedef struct _moxa_board_conf {
	int			boardType;
	int			numPorts;
	ulong			baseAddr;
	int			busType;
	moxa_pci_devinfo	pciInfo;
} moxa_board_conf;

static moxa_board_conf		moxa_boards[MAX_BOARDS];
static ulong	moxaBaseAddr[MAX_BOARDS];

struct moxa_str {
        int                     type;
	int			port;
	int			close_delay;
	unsigned short		closing_wait;
	int			count;
	int			blocked_open;
	int			event;
	int			asyncflags;
	long			session;
	long			pgrp;
	ulong			statusflags;
	struct tty_struct	*tty;
	struct termios		normal_termios;
	struct termios		callout_termios;
	struct wait_queue	*open_wait;
	struct wait_queue	*close_wait;
	struct tq_struct	tqueue;
};

struct mxser_mstatus{
       tcflag_t  cflag;
       int  cts;
       int  dsr;
       int  ri;
       int  dcd;
};

static  struct mxser_mstatus GMStatus[MAX_PORTS];

/* statusflags */
#define TXSTOPPED	0x1
#define LOWWAIT 	0x2
#define EMPTYWAIT	0x4
#define THROTTLE	0x8

/* event */
#define MOXA_EVENT_HANGUP	1

#define SERIAL_DO_RESTART


#define SERIAL_TYPE_NORMAL	1
#define SERIAL_TYPE_CALLOUT	2

#define WAKEUP_CHARS		256

#define PORTNO(x)		(MINOR((x)->device) - (x)->driver.minor_start)

static int verbose = 0;
static int ttymajor       = MOXAMAJOR;
static int calloutmajor   = MOXACUMAJOR;
#ifdef MODULE
/* Variables for insmod */
static int baseaddr[]      = {0, 0, 0, 0};
static int type[]         = {0, 0, 0, 0};
static int numports[]     = {0, 0, 0, 0};

# if (LINUX_VERSION_CODE > VERSION_CODE(2,1,11))
MODULE_AUTHOR("William Chen");
MODULE_DESCRIPTION("PCL Intellio Family Multiport Board Device Driver");
MODULE_PARM(type,        "1-4i");
MODULE_PARM(baseaddr,     "1-4i");
MODULE_PARM(numports,    "1-4i");
MODULE_PARM(ttymajor,    "i");
MODULE_PARM(calloutmajor,"i");
MODULE_PARM(verbose,"i");
# endif

#endif MODULE

struct tty_driver	moxaDriver;
struct tty_driver	moxaCallout;
struct tty_struct *	moxaTable[MAX_PORTS+1];
struct termios *	moxaTermios[MAX_PORTS+1];
struct termios *	moxaTermiosLocked[MAX_PORTS+1];
struct moxa_str 	moxaChannels[MAX_PORTS];
int			moxaRefcount;
f_uchar 		moxaXmitBuff;
static int		moxaTimer_on;
struct timer_list	moxaTimer = {NULL, NULL, 0, 0, 0};
static int		moxaEmptyTimer_on[MAX_PORTS];
struct timer_list	moxaEmptyTimer[MAX_PORTS];
struct semaphore	moxaBuffSem = MUTEX;


int		moxa_init(void);
#ifdef MODULE
int		init_module(void);
void		cleanup_module(void);
#endif
/*
 * static functions:
 */
static int moxa_get_PCI_conf(int ,int ,int ,moxa_board_conf *);
static void	do_moxa_softint(void *);
static int	moxa_open(struct tty_struct *, struct file *);
static void	moxa_close(struct tty_struct *, struct file *);
static int	moxa_write(struct tty_struct *, int, const unsigned char *, int);
static int	moxa_write_room(struct tty_struct *);
static void	moxa_flush_buffer(struct tty_struct *);
static int	moxa_chars_in_buffer(struct tty_struct *);
static void	moxa_flush_chars(struct tty_struct *);
static void	moxa_put_char(struct tty_struct *, unsigned char);
static int	moxa_ioctl(struct tty_struct *, struct file *, uint, ulong);
static void	moxa_throttle(struct tty_struct *);
static void	moxa_unthrottle(struct tty_struct *);
static void	moxa_set_termios(struct tty_struct *, struct termios *);
static void	moxa_stop(struct tty_struct *);
static void	moxa_start(struct tty_struct *);
static void	moxa_hangup(struct tty_struct *);
static void	moxa_poll(ulong);
static void	set_tty_param(struct tty_struct *);
static int	block_till_ready(struct tty_struct *, struct file *,
				 struct moxa_str *);
static void	setup_empty_event(struct tty_struct *);
static void	check_xmit_empty(unsigned long);
static void	shut_down(struct moxa_str *);
static void	receive_data(struct moxa_str *);
/*
 * moxa board interface functions:
 */
static void MoxaDriverInit(void);
static int  MoxaDriverIoctl(unsigned int, unsigned long, int);
static int  MoxaDriverPoll(void);
static int  MoxaPortsOfCard(int);
static int  MoxaPortIsValid(int);
static void MoxaPortEnable(int);
static void MoxaPortDisable(int);
static long MoxaPortGetMaxBaud(int);
static long MoxaPortSetBaud(int, long);
static int  MoxaPortSetTermio(int, struct termios *);
static int  MoxaPortGetLineOut(int, int *, int *);
static void MoxaPortLineCtrl(int, int, int);
static void MoxaPortFlowCtrl(int, int, int, int, int,int);
static int  MoxaPortLineStatus(int);
static int  MoxaPortDCDChange(int);
static int  MoxaPortDCDON(int);
static void MoxaPortFlushData(int, int);
static int  MoxaPortWriteData(int, f_uchar, int);
static int  MoxaPortReadData(int, f_uchar, int);
static int  MoxaPortTxQueue(int);
static int  MoxaPortRxQueue(int);
static int  MoxaPortTxFree(int);
static void MoxaPortTxDisable(int);
static void MoxaPortTxEnable(int);
static int  MoxaPortResetBrkCnt(int);
static void MoxaPortSendBreak(int, int);
static int  moxa_get_serial_info(struct moxa_str *, struct serial_struct *);
static int  moxa_set_serial_info(struct moxa_str *, struct serial_struct *);
static void MoxaSetFifo(int port, int enable);

#if 0
static long MoxaPortGetCurBaud(int);
static int  MoxaPortSetMode(int, int, int, int);
static int  MoxaPortTxBufSize(int);
static int  MoxaPortRxBufSize(int);
static int  MoxaPortGetBrkCnt(int);
static void MoxaPortSetXonXoff(int, int, int);
static int  MoxaPortIsTxHold(int);
static int  MoxaPortRxFree(int);
#endif

#ifdef MODULE
int init_module(void)
{
	int	ret;

	if (verbose)
		printk("Loading module moxa ...\n");
	ret = moxa_init();
	if (verbose)
		printk("Done\n");
	return (ret);
}

void
cleanup_module(void)
{
	int	i;

	if (verbose)
		printk("Unloading module moxa ...\n");

	if (moxaTimer_on)
		del_timer(&moxaTimer);

	for (i = 0; i < MAX_PORTS; i++)
		if (moxaEmptyTimer_on[i])
			del_timer(&moxaEmptyTimer[i]);

	if (tty_unregister_driver(&moxaCallout))
		printk("Couldn't unregister PCL Intellio family callout driver\n");
	if (tty_unregister_driver(&moxaDriver))
		printk("Couldn't unregister PCL Intellio family serial driver\n");
	if (verbose)
		printk("Done\n");

}
#endif

int moxa_init(void)
{
	int		i,n,index,numBoards;
	unsigned char	busnum,devnum;
	struct moxa_str *ch;
        int    ret1,ret2;

	printk("PCL Intellio family driver version %s\n",MOXA_VERSION);

	memset(&moxaDriver, 0, sizeof(struct tty_driver));
	memset(&moxaCallout, 0, sizeof(struct tty_driver));
	moxaDriver.magic = TTY_DRIVER_MAGIC;
	moxaDriver.name = "ttya";
	moxaDriver.major = ttymajor;
	moxaDriver.minor_start = 0;
	moxaDriver.num = MAX_PORTS + 1;
	moxaDriver.type = TTY_DRIVER_TYPE_SERIAL;
	moxaDriver.subtype = SERIAL_TYPE_NORMAL;
	moxaDriver.init_termios = tty_std_termios;
	moxaDriver.init_termios.c_iflag = 0;
	moxaDriver.init_termios.c_oflag = 0;
	moxaDriver.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
	moxaDriver.init_termios.c_lflag = 0;
	moxaDriver.flags = TTY_DRIVER_REAL_RAW;
	moxaDriver.refcount = &moxaRefcount;
	moxaDriver.table = moxaTable;
	moxaDriver.termios = moxaTermios;
	moxaDriver.termios_locked = moxaTermiosLocked;

	moxaDriver.open = moxa_open;
	moxaDriver.close = moxa_close;
	moxaDriver.write = moxa_write;
	moxaDriver.write_room = moxa_write_room;
	moxaDriver.flush_buffer = moxa_flush_buffer;
	moxaDriver.chars_in_buffer = moxa_chars_in_buffer;
	moxaDriver.flush_chars = moxa_flush_chars;
	moxaDriver.put_char = moxa_put_char;
	moxaDriver.ioctl = moxa_ioctl;
	moxaDriver.throttle = moxa_throttle;
	moxaDriver.unthrottle = moxa_unthrottle;
	moxaDriver.set_termios = moxa_set_termios;
	moxaDriver.stop = moxa_stop;
	moxaDriver.start = moxa_start;
	moxaDriver.hangup = moxa_hangup;

	moxaCallout = moxaDriver;
	moxaCallout.name = "ttyA";
	moxaCallout.major = calloutmajor;
	moxaCallout.subtype = SERIAL_TYPE_CALLOUT;

	moxaXmitBuff = 0;

	for ( i=0, ch=moxaChannels; i<MAX_PORTS; i++, ch++ ) {
            ch->type = PORT_16550A;
	    ch->port = i;
	    ch->tqueue.routine = do_moxa_softint;
	    ch->tqueue.data = ch;
	    ch->tty = 0;
	    ch->close_delay = 5*HZ/10;
	    ch->closing_wait = 30*HZ;
	    ch->count = 0;
	    ch->blocked_open = 0;
	    ch->callout_termios = moxaCallout.init_termios;
	    ch->normal_termios = moxaDriver.init_termios;
	    ch->open_wait = 0;
	    ch->close_wait = 0;
	}

	for ( i=0; i< MAX_BOARDS; i++ ) {
	    moxa_boards[i].boardType = 0;
	    moxa_boards[i].numPorts = 0;
	    moxa_boards[i].baseAddr = 0;
	    moxa_boards[i].busType = 0;
	    moxa_boards[i].pciInfo.busNum = 0;
	    moxa_boards[i].pciInfo.devNum = 0;
	}
	MoxaDriverInit();
	printk("Tty devices major number = %d, callout devices major number = %d\n",ttymajor,calloutmajor);

        ret1 = 0;
        ret2 = 0;
        if ( (ret1=tty_register_driver(&moxaDriver)) ){
	    printk("Couldn't install PCL Smartio family driver !\n");
        }else if ( (ret2=tty_register_driver(&moxaCallout)) ){
            tty_unregister_driver(&moxaDriver);
            printk("Couldn't install PCL Smartio family callout driver !\n");
        }

        if(ret1 || ret2){
            return -1;
        }

	for ( i=0; i<MAX_PORTS; i++ ) {
	    init_timer(&moxaEmptyTimer[i]);
	    moxaEmptyTimer[i].function = check_xmit_empty;
	    moxaEmptyTimer[i].data = (ulong)&moxaChannels[i];
	    moxaEmptyTimer_on[i] = 0;
	}

	init_timer(&moxaTimer);
	moxaTimer.function = moxa_poll;
	moxaTimer.expires  = jiffies + (HZ/50);
	moxaTimer_on = 1;
	add_timer(&moxaTimer);

/* Find the boards defined in source code */
	numBoards = 0;
	for ( i = 0; i < MAX_BOARDS; i++) {
	    if ((moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) ||
	        (moxa_isa_boards[i].boardType == MOXA_BOARD_C320_ISA)) {
	        moxa_boards[numBoards].boardType = moxa_isa_boards[i].boardType;
		if (moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA)
	            moxa_boards[numBoards].numPorts = 8;
		else
	            moxa_boards[numBoards].numPorts=moxa_isa_boards[i].numPorts;
	        moxa_boards[numBoards].busType = MOXA_BUS_TYPE_ISA;
	        moxa_boards[numBoards].baseAddr = moxa_isa_boards[i].baseAddr;
		if (verbose)
	            printk("Board %2d: %s board(baseAddr=%lx)\n",
                        numBoards+1,
			moxa_brdname[moxa_boards[numBoards].boardType-1],
			moxa_boards[numBoards].baseAddr);
		numBoards++;
	    }
	}
/* Find the boards defined form module args. */
#ifdef MODULE
	for ( i = 0; i < MAX_BOARDS; i++) {
	    if ((type[i]==MOXA_BOARD_C218_ISA)||
		(type[i] == MOXA_BOARD_C320_ISA)) {
		if (verbose)
	            printk("Board %2d: %s board(baseAddr=%lx)\n",
                                numBoards+1,
				moxa_brdname[type[i]-1],
				(ulong)baseaddr[i]);
		if (numBoards >= MAX_BOARDS) {
		    if (verbose)
		        printk("More than %d PCL Intellio family boards found. Board is ignored.",MAX_BOARDS);
		    continue;
		}
	        moxa_boards[numBoards].boardType = type[i];
		if (moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA)
	            moxa_boards[numBoards].numPorts = 8;
		else
	            moxa_boards[numBoards].numPorts = numports[i];
	        moxa_boards[numBoards].busType = MOXA_BUS_TYPE_ISA;
	        moxa_boards[numBoards].baseAddr = baseaddr[i];
		numBoards++;
	    }
	}
#endif
/* start to finding PCI board here */
#ifdef CONFIG_PCI

#if (LINUX_VERSION_CODE < VERSION_CODE(2,1,0))
	if (pcibios_present()) {
#else
	if (pci_present()) {
#endif
	    n = sizeof (moxa_pcibrds)/sizeof (moxa_pciinfo);
	    index = 0;
	    i = 0;
	    while (i < n) {
	          if (pcibios_find_device(moxa_pcibrds[i].vendor_id,
				moxa_pcibrds[i].device_id,
				index,
				&busnum,
				&devnum) != 0) {
	               i++;
		       index = 0;
		       continue;
	           }
		   if (verbose)
	               printk("Board %2d: %s board(BusNo=%d,DevNo=%d)\n",
                                numBoards+1,
				moxa_brdname[moxa_pcibrds[i].board_type-1],
				busnum,
				devnum >> 3);
	           index++;
	           if ( numBoards >= MAX_BOARDS) {
		       if (verbose)
		           printk("More than %d PCL Intellio family boards found. Board is ignored.",MAX_BOARDS);
		    }
		    else {
		        moxa_get_PCI_conf(busnum,devnum,
		        			moxa_pcibrds[i].board_type,
						&moxa_boards[numBoards]);
		        numBoards++;
		    }
	    }
	}
#endif
	for ( i = 0; i < numBoards; i++) {

#ifndef __alpha__
	    if (moxa_boards[i].baseAddr >= 0x100000) { 
		moxaBaseAddr[i] =
		    (ulong) ioremap((ulong)moxa_boards[i].baseAddr,0x4000);
	    }
	    else 
#endif
		moxaBaseAddr[i] = moxa_boards[i].baseAddr;
	}

	return(0);
}

static int moxa_get_PCI_conf(int busnum,int devnum,int board_type,moxa_board_conf *board)
{
	unsigned int	val;

	pcibios_read_config_dword(busnum,devnum,PCI_BASE_ADDRESS_2,&val);
	if (val == 0xffffffff)
		return (-1);
	else 
		board->baseAddr = val & PCI_BASE_ADDRESS_MEM_MASK;
	board->boardType = board_type;
	switch (board_type) {
	case 	MOXA_BOARD_C218_ISA :
	case 	MOXA_BOARD_C218_PCI :
		board->numPorts = 8;
		break;

	case 	MOXA_BOARD_CP204J :
		board->numPorts = 4;
		break;
	default :
		board->numPorts = 0;
		break;
	}
	board->busType = MOXA_BUS_TYPE_PCI;
	board->pciInfo.busNum = busnum;
	board->pciInfo.devNum = devnum >> 3;

	return(0);
}

static void do_moxa_softint(void *private_)
{
	struct moxa_str 	*ch = (struct moxa_str *)private_;
	struct tty_struct	*tty;

	if ( !ch || !(tty = ch->tty) )
	    return;
#if (LINUX_VERSION_CODE <  VERSION_CODE(2,1,0))
	if ( clear_bit(MOXA_EVENT_HANGUP, &ch->event) ) {
#else
	if ( test_and_clear_bit(MOXA_EVENT_HANGUP, &ch->event) ) {
#endif
	    tty_hangup(tty);
	    wake_up_interruptible(&ch->open_wait);
	    ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
	}
}

static int moxa_open(struct tty_struct *tty, struct file * filp)
{
	struct moxa_str *ch;
	int		port;
	int		retval;
	unsigned long	page;

	port = PORTNO(tty);
	if ( port == MAX_PORTS ) {
	    MOD_INC_USE_COUNT;
	    return(0);
	}
	if ( !MoxaPortIsValid(port) ) {
	    tty->driver_data = NULL;
	    return(-ENODEV);
	}

	down(&moxaBuffSem);
	if ( !moxaXmitBuff ) {
	    page = get_free_page(GFP_KERNEL);
	    if ( !page ) {
		up(&moxaBuffSem);
		return(-ENOMEM);
	    }
	    if ( moxaXmitBuff )
		free_page(page);
	    else
		moxaXmitBuff = (f_uchar)page;
	}
	up(&moxaBuffSem);

	MOD_INC_USE_COUNT;
	ch = &moxaChannels[port];
	ch->count++;
	tty->driver_data = ch;
	ch->tty = tty;
	if ( ch->count == 1 && (ch->asyncflags & ASYNC_SPLIT_TERMIOS) ) {
	    if ( tty->driver.subtype == SERIAL_TYPE_NORMAL )
		*tty->termios = ch->normal_termios;
	    else
		*tty->termios = ch->callout_termios;
	}
	ch->session = current->session;
	ch->pgrp = current->pgrp;
	if ( !(ch->asyncflags & ASYNC_INITIALIZED) ) {
	    ch->statusflags = 0;
	    set_tty_param(tty);
	    MoxaPortLineCtrl(ch->port, 1, 1);
	    MoxaPortEnable(ch->port);
	    ch->asyncflags |= ASYNC_INITIALIZED;
	}

	retval = block_till_ready(tty, filp, ch);

        moxa_unthrottle(tty);

	if(ch->type == PORT_16550A){
		MoxaSetFifo(ch->port, 1);
	}else{
		MoxaSetFifo(ch->port, 0);
	}

	return(retval);
}

static void moxa_close(struct tty_struct * tty, struct file * filp)
{
	struct moxa_str *ch;
	int		port;

	port = PORTNO(tty);
	if ( port == MAX_PORTS ) {
	    MOD_DEC_USE_COUNT;
	    return;
	}

	if ( !MoxaPortIsValid(port) ) {
#ifdef SERIAL_DEBUG_CLOSE
	    printk("Invalid portno in moxa_close\n");
#endif
	    tty->driver_data = NULL;
	    return;
	}
	if ( tty->driver_data == NULL ) {
	    return;
	}
	if ( tty_hung_up_p(filp) ) {
            MOD_DEC_USE_COUNT;
	    return;
	}

	ch = (struct moxa_str *)tty->driver_data;

	if ( (tty->count == 1) && (ch->count != 1) ) {
	    printk("moxa_close: bad serial port count; tty->count is 1, "
		   "ch->count is %d\n", ch->count);
	    ch->count = 1;
	}
	if ( --ch->count < 0 ) {
	    printk("moxa_close: bad serial port count, minor=%d\n",
		   MINOR(tty->device));
	    ch->count = 0;
	}
	if ( ch->count ) {
            MOD_DEC_USE_COUNT;
	    return;
	}

	ch->asyncflags |= ASYNC_CLOSING;

	/*
	 * Save the termios structure, since this port may have
	 * separate termios for callout and dialin.
	 */
	if ( ch->asyncflags & ASYNC_NORMAL_ACTIVE )
	    ch->normal_termios = *tty->termios;
	if ( ch->asyncflags & ASYNC_CALLOUT_ACTIVE )
	    ch->callout_termios = *tty->termios;
	if ( ch->asyncflags & ASYNC_INITIALIZED ) {
	    setup_empty_event(tty);
	    tty_wait_until_sent(tty, 30*HZ);	/* 30 seconds timeout */
	    moxaEmptyTimer_on[ch->port] = 0;
	    del_timer(&moxaEmptyTimer[ch->port]);
	}

	shut_down(ch);
	MoxaPortFlushData(port, 2);

	if ( tty->driver.flush_buffer )
	    tty->driver.flush_buffer(tty);
	if ( tty->ldisc.flush_buffer )
	    tty->ldisc.flush_buffer(tty);
	tty->closing = 0;
	ch->event = 0;
	ch->tty = 0;
	/*********** delete by Linux 2.0.0 serial.c, why ???? *******
	if ( tty->ldisc.num != ldiscs[N_TTY].num ) {
	    if ( tty->ldisc.close )
		(tty->ldisc.close)(tty);
	    tty->ldisc = ldiscs[N_TTY];
	    tty->termios->c_line = N_TTY;
	    if ( tty->ldisc.open )
		(tty->ldisc.open)(tty);
	}
	***************************************************************/
	if ( ch->blocked_open ) {
	    if ( ch->close_delay ) {
		current->state = TASK_INTERRUPTIBLE;
		schedule_timeout(ch->close_delay);
	    }
	    wake_up_interruptible(&ch->open_wait);
	}
	ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
			    ASYNC_CLOSING);
	wake_up_interruptible(&ch->close_wait);
        MOD_DEC_USE_COUNT;
}

static int moxa_write(struct tty_struct *tty, int from_user,
		      const unsigned char * buf, int count)
{
	struct moxa_str *ch;
	int		len, port;
	ulong		flags;
	f_uchar 	temp;

	ch = (struct moxa_str *)tty->driver_data;
	if ( ch == NULL )
	    return(0);
	port = ch->port;
	save_flags(flags);
	cli();
	if ( from_user ) {
	    copy_from_user(moxaXmitBuff, buf, count);
	    temp = moxaXmitBuff;
	} else
	    temp = (f_uchar)buf;
	len = MoxaPortWriteData(port, temp, count);
	restore_flags(flags);
	/*********************************************
	if ( !(ch->statusflags & LOWWAIT) &&
	     ((len != count) || (MoxaPortTxFree(port) <= 100)) )
	************************************************/
	    ch->statusflags |= LOWWAIT;
	return(len);
}

static int moxa_write_room(struct tty_struct *tty)
{
	struct moxa_str *ch;

	if ( tty->stopped )
	    return(0);
	ch = (struct moxa_str *)tty->driver_data;
	if ( ch == NULL )
	    return(0);
	return(MoxaPortTxFree(ch->port));
}

static void moxa_flush_buffer(struct tty_struct *tty)
{
	struct moxa_str *ch = (struct moxa_str *)tty->driver_data;

	if ( ch == NULL )
	    return;
	MoxaPortFlushData(ch->port, 1);
	if ( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
	     tty->ldisc.write_wakeup )
	    (tty->ldisc.write_wakeup)(tty);
	wake_up_interruptible(&tty->write_wait);
}

static int moxa_chars_in_buffer(struct tty_struct *tty)
{
	int		chars;
	struct moxa_str *ch = (struct moxa_str *)tty->driver_data;

	/*
	 * Sigh...I have to check if driver_data is NULL here, because
	 * if an open() fails, the TTY subsystem eventually calls
	 * tty_wait_until_sent(), which calls the driver's chars_in_buffer()
	 * routine.  And since the open() failed, we return 0 here.  TDJ
	 */
	if ( ch == NULL )
	    return(0);
	chars = MoxaPortTxQueue(ch->port);
	if ( chars ) {
	    /*
	     * Make it possible to wakeup anything waiting for output
	     * in tty_ioctl.c, etc.
	     */
	    if ( !(ch->statusflags & EMPTYWAIT) )
		setup_empty_event(tty);
	}
	return(chars);
}

static void moxa_flush_chars(struct tty_struct *tty)
{
	/*
	 * Don't think I need this, because this is called to empty the TX
	 * buffer for the 16450, 16550, etc.
	 */
}

static void moxa_put_char(struct tty_struct *tty, unsigned char c)
{
	struct moxa_str *ch;
	int		port;
	ulong		flags;

	ch = (struct moxa_str *)tty->driver_data;
	if ( ch == NULL )
	    return;
	port = ch->port;
	save_flags(flags);
	cli();
	moxaXmitBuff[0] = c;
	MoxaPortWriteData(port, moxaXmitBuff, 1);
	restore_flags(flags);
	/************************************************
	if ( !(ch->statusflags & LOWWAIT) && (MoxaPortTxFree(port) <= 100) )
	*************************************************/
	    ch->statusflags |= LOWWAIT;
}

static int moxa_ioctl(struct tty_struct *tty, struct file * file,
		      unsigned int cmd, unsigned long arg)
{
	struct moxa_str *ch = (struct moxa_str *)tty->driver_data;
	register int	port;
	int		retval, dtr, rts;
	ulong		flag;

	port = PORTNO(tty);
	if ( (port != MAX_PORTS) && (!ch) )
	    return(-EINVAL);

	switch ( cmd ) {
	case TCSBRK:	/* SVID version: non-zero arg --> no break */
	    retval = tty_check_change(tty);
	    if ( retval )
		return(retval);
	    setup_empty_event(tty);
	    tty_wait_until_sent(tty, 0);
	    if ( !arg )
		MoxaPortSendBreak(ch->port, 0);
	    return(0);
	case TCSBRKP:	/* support for POSIX tcsendbreak() */
	    retval = tty_check_change(tty);
	    if ( retval )
		return(retval);
	    setup_empty_event(tty);
	    tty_wait_until_sent(tty, 0);
	    MoxaPortSendBreak(ch->port, arg);
	    return(0);
	case TIOCGSOFTCAR:
	    retval = verify_area(VERIFY_WRITE, (void *)arg, sizeof(long));
	    if ( retval )
		return(retval);
	    put_to_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg);
	    return(0);
	case TIOCSSOFTCAR:
	    retval = verify_area(VERIFY_READ, (void *)arg, sizeof(long));
	    if ( retval )
		return(retval);
	    get_from_user(retval,(unsigned long *)arg);
	    arg = retval;
	    tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) |
				    (arg ? CLOCAL : 0));
	    if ( C_CLOCAL(tty) )
		ch->asyncflags &= ~ASYNC_CHECK_CD;
	    else
		ch->asyncflags |= ASYNC_CHECK_CD;
	    return(0);
	case TIOCMGET:
	    retval = verify_area(VERIFY_WRITE, (void *)arg, sizeof(long));
	    if ( retval )
		return(retval);
	    flag = 0;
	    MoxaPortGetLineOut(ch->port, &dtr, &rts);
	    if ( dtr )
		flag |= TIOCM_DTR;
	    if ( rts )
		flag |= TIOCM_RTS;
	    dtr = MoxaPortLineStatus(ch->port);
	    if ( dtr & 1 )
		flag |= TIOCM_CTS;
	    if ( dtr & 2 )
		flag |= TIOCM_DSR;
	    if ( dtr & 4 )
		flag |= TIOCM_CD;
	    put_to_user(flag, (ulong *)arg);
	    return(0);
	case TIOCMBIS:
	    retval = verify_area(VERIFY_READ, (void *)arg, sizeof(long));
	    if ( retval )
		return(retval);
	    get_from_user(retval,(unsigned long *)arg);
	    MoxaPortGetLineOut(ch->port, &dtr, &rts);
	    if ( retval & TIOCM_RTS )
		rts = 1;
	    if ( retval & TIOCM_DTR )
		dtr = 1;
	    MoxaPortLineCtrl(ch->port, dtr, rts);
	    return(0);
	case TIOCMBIC:
	    retval = verify_area(VERIFY_READ, (void *)arg, sizeof(long));
	    if ( retval )
		return(retval);
	    get_from_user(retval,(unsigned long *)arg);
	    MoxaPortGetLineOut(ch->port, &dtr, &rts);
	    if ( retval & TIOCM_RTS )
		rts = 0;
	    if ( retval & TIOCM_DTR )
		dtr = 0;
	    MoxaPortLineCtrl(ch->port, dtr, rts);
	    return(0);
	case TIOCMSET:
	    retval = verify_area(VERIFY_READ, (void *)arg, sizeof(long));
	    if ( retval )
		return(retval);
	    get_from_user(retval,(unsigned long *)arg);
	    dtr = rts = 0;
	    if ( retval & TIOCM_RTS )
		rts = 1;
	    if ( retval & TIOCM_DTR )
		dtr = 1;
	    MoxaPortLineCtrl(ch->port, dtr, rts);
	    return(0);
	case TIOCGSERIAL:
	    retval = verify_area(VERIFY_WRITE, (void *)arg,
				sizeof(struct serial_struct));
	    if ( retval )
		return(retval);
	    return(moxa_get_serial_info(ch, (struct serial_struct *)arg));

	case TIOCSSERIAL:
	    retval = verify_area(VERIFY_READ, (void *)arg,
				sizeof(struct serial_struct));
	    if ( retval )
		return(retval);
	    return(moxa_set_serial_info(ch, (struct serial_struct *)arg));
	default:
	    retval = MoxaDriverIoctl(cmd, arg, port);
	}
	return(retval);
}

static void moxa_throttle(struct tty_struct * tty)
{
	struct moxa_str *ch = (struct moxa_str *)tty->driver_data;

	ch->statusflags |= THROTTLE;
}

static void moxa_unthrottle(struct tty_struct * tty)
{
	struct moxa_str *ch = (struct moxa_str *)tty->driver_data;

	ch->statusflags &= ~THROTTLE;
}

static void moxa_set_termios(struct tty_struct *tty,
			     struct termios *old_termios)
{
	struct moxa_str *ch = (struct moxa_str *)tty->driver_data;

	if ( ch == NULL )
	    return;
	set_tty_param(tty);
	if ( !(old_termios->c_cflag & CLOCAL) &&
	     (tty->termios->c_cflag & CLOCAL) )
	    wake_up_interruptible(&ch->open_wait);
}

static void moxa_stop(struct tty_struct *tty)
{
	struct moxa_str *ch = (struct moxa_str *)tty->driver_data;

	if ( ch == NULL )
	    return;
	MoxaPortTxDisable(ch->port);
	ch->statusflags |= TXSTOPPED;
}


static void moxa_start(struct tty_struct *tty)
{
	struct moxa_str *ch = (struct moxa_str *)tty->driver_data;

	if ( ch == NULL )
	    return;

	if ( !(ch->statusflags & TXSTOPPED) )
	    return;

	MoxaPortTxEnable(ch->port);
	ch->statusflags &= ~TXSTOPPED;
}

static void moxa_hangup(struct tty_struct *tty)
{
	struct moxa_str *ch = (struct moxa_str *)tty->driver_data;

	moxa_flush_buffer(tty);
	shut_down(ch);
	ch->event = 0;
	ch->count = 0;
	ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
	ch->tty = 0;
	wake_up_interruptible(&ch->open_wait);
}

static void moxa_poll(ulong ignored)
{
	register int		card;
	struct moxa_str 	*ch;
	struct tty_struct	*tp;
	int			i, ports;

	moxaTimer_on = 0;
	del_timer(&moxaTimer);

	if ( MoxaDriverPoll() < 0 ) {
	    moxaTimer.function = moxa_poll;
	    moxaTimer.expires  = jiffies + (HZ/50);
	    moxaTimer_on = 1;
	    add_timer(&moxaTimer);
	    return;
	}
	for ( card=0; card<MAX_BOARDS; card++ ) {
	    if ( (ports = MoxaPortsOfCard(card)) <= 0 )
		continue;
	    ch = &moxaChannels[card * MAX_PORTS_PER_BOARD];
	    for ( i=0; i<ports; i++, ch++ ) {
		if ( (ch->asyncflags & ASYNC_INITIALIZED) == 0 )
		    continue;
		if ( !(ch->statusflags & THROTTLE) &&
		     (MoxaPortRxQueue(ch->port) > 0) )
		    receive_data(ch);
		if ( (tp = ch->tty) == 0 )
		    continue;
		if ( ch->statusflags & LOWWAIT ) {
		    if ( MoxaPortTxQueue(ch->port) <= WAKEUP_CHARS ) {
			if ( !tp->stopped ) {
			    ch->statusflags &= ~LOWWAIT;
			    if ( (tp->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
				 tp->ldisc.write_wakeup )
				(tp->ldisc.write_wakeup)(tp);
			    wake_up_interruptible(&tp->write_wait);
			}
		    }
		}
		if ( !I_IGNBRK(tp)&&(MoxaPortResetBrkCnt(ch->port) > 0) ) {
		    tty_insert_flip_char(tp, 0, TTY_BREAK);
		    tty_schedule_flip(tp);
		}
		if ( MoxaPortDCDChange(ch->port) ) {
		    if ( ch->asyncflags & ASYNC_CHECK_CD ) {
			if ( MoxaPortDCDON(ch->port) )
			    wake_up_interruptible(&ch->open_wait);
			else {
	       			set_bit(MOXA_EVENT_HANGUP,&ch->event);
#if (LINUX_VERSION_CODE >= VERSION_CODE(2,1,0))
				queue_task(&ch->tqueue, &tq_scheduler);
#else
				queue_task_irq_off(&ch->tqueue,&tq_scheduler);
#endif
			}
		    }
		}
	    }
	}

	moxaTimer.function = moxa_poll;
	moxaTimer.expires  = jiffies + (HZ/50);
	moxaTimer_on = 1;
	add_timer(&moxaTimer);
}

/******************************************************************************/

static void set_tty_param(struct tty_struct *tty)
{
	register struct termios *ts;
	struct moxa_str 	*ch;
	int			rts, cts, txflow, rxflow,xany;

	ch = (struct moxa_str *)tty->driver_data;
	ts = tty->termios;
	if ( ts->c_cflag & CLOCAL )
	    ch->asyncflags &= ~ASYNC_CHECK_CD;
	else
	    ch->asyncflags |= ASYNC_CHECK_CD;
	rts = cts = txflow = rxflow = xany = 0;
	if ( ts->c_cflag & CRTSCTS )
	    rts = cts = 1;
	if ( ts->c_iflag & IXON )
	    txflow = 1;
	if ( ts->c_iflag & IXOFF )
	    rxflow = 1;
	if ( ts->c_iflag & IXANY )
	    xany = 1;
	MoxaPortFlowCtrl(ch->port, rts, cts, txflow, rxflow,xany);
	MoxaPortSetTermio(ch->port, ts);
}

static int block_till_ready(struct tty_struct *tty, struct file *filp,
			    struct moxa_str *ch)
{
	struct wait_queue	wait = {current, NULL};
	int			retval;
	int			do_clocal = C_CLOCAL(tty);

	/*
	 * If the device is in the middle of being closed, then block
	 * until it's done, and then try again.
	 */
	if ( tty_hung_up_p(filp) || (ch->asyncflags & ASYNC_CLOSING) ) {
	    if ( ch->asyncflags & ASYNC_CLOSING )
		interruptible_sleep_on(&ch->close_wait);
#ifdef SERIAL_DO_RESTART
	    if ( ch->asyncflags & ASYNC_HUP_NOTIFY )
		return(-EAGAIN);
	    else
		return(-ERESTARTSYS);
#else
	    return(-EAGAIN);
#endif
	}

	/*
	 * If this is a callout device, then just make sure the normal
	 * device isn't being used.
	 */
	if ( tty->driver.subtype == SERIAL_TYPE_CALLOUT ) {
	    if ( ch->asyncflags & ASYNC_NORMAL_ACTIVE )
		return(-EBUSY);
	    if ( (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) &&
		 (ch->asyncflags & ASYNC_SESSION_LOCKOUT) &&
		 (ch->session != current->session) )
		return(-EBUSY);
	    if ( (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) &&
		 (ch->asyncflags & ASYNC_PGRP_LOCKOUT) &&
		 (ch->pgrp != current->pgrp) )
		return(-EBUSY);
	    ch->asyncflags |= ASYNC_CALLOUT_ACTIVE;
	    return(0);
	}

	/*
	 * If non-blocking mode is set, then make the check up front
	 * and then exit.
	 */
	if ( filp->f_flags & O_NONBLOCK ) {
	    if ( ch->asyncflags & ASYNC_CALLOUT_ACTIVE )
		return(-EBUSY);
	    ch->asyncflags |= ASYNC_NORMAL_ACTIVE;
	    return(0);
	}

	/*
	 * Block waiting for the carrier detect and the line to become free
	 */
	retval = 0;
	add_wait_queue(&ch->open_wait, &wait);
#ifdef SERIAL_DEBUG_OPEN
	printk("block_til_ready before block: ttys%d, count = %d\n",
	       ch->line, ch->count);
#endif
	cli();
	if ( !tty_hung_up_p(filp) )
	    ch->count--;
	sti();
	ch->blocked_open++;
	while ( 1 ) {
	    current->state = TASK_INTERRUPTIBLE;
	    if ( tty_hung_up_p(filp) ||
		 !(ch->asyncflags & ASYNC_INITIALIZED) ) {
#ifdef SERIAL_DO_RESTART
		if ( ch->asyncflags & ASYNC_HUP_NOTIFY )
		    retval = -EAGAIN;
		else
		    retval = -ERESTARTSYS;
#else
		retval = -EAGAIN;
#endif
		break;
	    }

	    if ( !(ch->asyncflags & ASYNC_CALLOUT_ACTIVE) &&
		 !(ch->asyncflags & ASYNC_CLOSING) && (do_clocal ||
		 MoxaPortDCDON(ch->port)) )
		break;

	    if ( signal_pending(current)) {
		retval = -ERESTARTSYS;
		break;
	    }

	    schedule();
	}
	current->state = TASK_RUNNING;
	remove_wait_queue(&ch->open_wait, &wait);
	if ( !tty_hung_up_p(filp) )
	    ch->count++;
	ch->blocked_open--;
#ifdef SERIAL_DEBUG_OPEN
	printk("block_til_ready after blocking: ttys%d, count = %d\n",
	       ch->line, ch->count);
#endif
	if ( retval )
	    return(retval);
	ch->asyncflags |= ASYNC_NORMAL_ACTIVE;
	return(0);
}

static void setup_empty_event(struct tty_struct *tty)
{
	struct moxa_str *ch = tty->driver_data;
	ulong		flags;

	save_flags(flags);
	cli();
	ch->statusflags |= EMPTYWAIT;
	moxaEmptyTimer_on[ch->port] = 0;
	del_timer(&moxaEmptyTimer[ch->port]);
	moxaEmptyTimer[ch->port].expires = jiffies + HZ;
	moxaEmptyTimer_on[ch->port] = 1;
	add_timer(&moxaEmptyTimer[ch->port]);
	restore_flags(flags);
}

static void check_xmit_empty(ulong data)
{
	struct moxa_str *ch;

	ch = (struct moxa_str *)data;
	moxaEmptyTimer_on[ch->port] = 0;
	del_timer(&moxaEmptyTimer[ch->port]);
	if ( ch->tty && (ch->statusflags & EMPTYWAIT) ) {
	    if ( MoxaPortTxQueue(ch->port) == 0 ) {
		ch->statusflags &= ~EMPTYWAIT;
		if ( (ch->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
		     ch->tty->ldisc.write_wakeup )
		    (ch->tty->ldisc.write_wakeup)(ch->tty);
		wake_up_interruptible(&ch->tty->write_wait);
		return;
	    }
	    moxaEmptyTimer[ch->port].expires = jiffies + HZ;
	    moxaEmptyTimer_on[ch->port] = 1;
	    add_timer(&moxaEmptyTimer[ch->port]);
	} else
	    ch->statusflags &= ~EMPTYWAIT;
}

static void shut_down(struct moxa_str *ch)
{
	struct tty_struct	*tp;

	if ( !(ch->asyncflags & ASYNC_INITIALIZED) )
	    return;

	tp = ch->tty;

	MoxaPortDisable(ch->port);

	/*
	 * If we're a modem control device and HUPCL is on, drop RTS & DTR.
	 */
	if ( tp->termios->c_cflag & HUPCL )
	    MoxaPortLineCtrl(ch->port, 0, 0);

	ch->asyncflags &= ~ASYNC_INITIALIZED;
}

static void receive_data(struct moxa_str *ch)
{
	struct tty_struct	*tp;
	struct termios		*ts;
	int			i, count, rc, space;
	f_uchar 		charptr, flagptr;
	ulong			flags;

	ts = 0;
	tp = ch->tty;
	if ( tp )
	    ts = tp->termios;
	/**************************************************
	if ( !tp || !ts || !(ts->c_cflag & CREAD) ) {
	*****************************************************/
	if ( !tp || !ts ) {
	    MoxaPortFlushData(ch->port,0);
	    return;
	}
	space = TTY_FLIPBUF_SIZE - tp->flip.count;
	if ( space <= 0 )
	    return;
	charptr = tp->flip.char_buf_ptr;
	flagptr = tp->flip.flag_buf_ptr;
	rc = tp->flip.count;
	save_flags(flags);
	cli();
	count = MoxaPortReadData(ch->port, charptr, space);
	restore_flags(flags);
	for ( i=0; i<count; i++ )
	    *flagptr++ = 0;
	charptr += count;
	rc += count;
	tp->flip.count = rc;
	tp->flip.char_buf_ptr = charptr;
	tp->flip.flag_buf_ptr = flagptr;
	tty_schedule_flip(ch->tty);
}

/*****************************************************************************
 *									     *
 *    Application <---> Driver : data/memory copy functions		     *
 *									     *
 *    These sub-routines called by functions of moxa.obj. You can't modify   *
 *    the names of these sub-routines. You are able to modify the contents   *
 *    of these routines if the Linux version upgraded and those functions    *
 *    memcpy_fromfs, memcpy_tofs, ... have been not supported.		     *
 *									     *
 *****************************************************************************/
static unsigned long moxa_jiffies(void)
{
	return(jiffies);
}

static int  moxa_jiffiesHz(void)
{
	return(HZ);
}

static void moxa_memcpy_fromfs(f_uchar target, f_uchar source, int len)
{
	copy_from_user(target, source, len);
}

static void moxa_memcpy_tofs(f_uchar target, f_uchar source, int len)
{
	copy_to_user(target, source, len);
}

static void moxa_put_fs_long(int value, f_ulong *target)
{
	put_to_user(value, target);
}

static int  moxa_verify_area(int mode, void *buffer, int len)
{
	return( verify_area(mode, buffer, len) );
}
/**************************************************************************
 **************************************************************************/

#define Magic_code	0x404

/*
 *	System Configuration
 */
/*
 *	for C218 BIOS initialization
 */
#define C218_ConfBase	0x800
#define C218_status	(C218_ConfBase + 0)	/* BIOS running status	*/
#define C218_diag	(C218_ConfBase + 2)	/* diagnostic status	*/
#define C218_key	(C218_ConfBase + 4)	/* WORD (0x218 for C218)*/
#define C218DLoad_len	(C218_ConfBase + 6)	/* WORD 		*/
#define C218check_sum	(C218_ConfBase + 8)	/* BYTE 		*/
#define C218chksum_ok	(C218_ConfBase + 0x0a)	/* BYTE (1:ok)		*/
#define C218_TestRx	(C218_ConfBase + 0x10)	/* 8 bytes for 8 ports	*/
#define C218_TestTx	(C218_ConfBase + 0x18)	/* 8 bytes for 8 ports	*/
#define C218_RXerr	(C218_ConfBase + 0x20)	/* 8 bytes for 8 ports	*/
#define C218_ErrFlag	(C218_ConfBase + 0x28)	/* 8 bytes for 8 ports	*/

#define C218_LoadBuf	0x0F00
#define C218_KeyCode	0x218
#define CP204J_KeyCode	0x204

/*
 *	for C320 BIOS initialization
 */
#define C320_ConfBase	0x800
#define C320_LoadBuf	0x0f00
#define STS_init	0x05			/* for C320_status	*/

#define C320_status	C320_ConfBase + 0	/* BIOS running status	*/
#define C320_diag	C320_ConfBase + 2	/* diagnostic status	*/
#define C320_key	C320_ConfBase + 4	/* WORD (0320H for C320)*/
#define C320DLoad_len	C320_ConfBase + 6	/* WORD 		*/
#define C320check_sum	C320_ConfBase + 8	/* WORD 		*/
#define C320chksum_ok	C320_ConfBase + 0x0a	/* WORD (1:ok)		*/
#define C320bapi_len	C320_ConfBase + 0x0c	/* WORD 		*/
#define C320UART_no	C320_ConfBase + 0x0e	/* WORD 		*/

#define C320_KeyCode	0x320

#define FixPage_addr	0x0000		/* starting addr of static page  */
#define DynPage_addr	0x2000		/* starting addr of dynamic page */
#define C218_start	0x3000		/* starting addr of C218 BIOS prg*/
#define Control_reg	0x1ff0		/* select page and reset control */
#define HW_reset	0x80

/*
 *	Function Codes
 */
#define FC_CardReset	0x80
#define FC_ChannelReset 1		/* C320 firmware not supported */
#define FC_EnableCH	2
#define FC_DisableCH	3
#define FC_SetParam	4
#define FC_SetMode	5
#define FC_SetRate	6
#define FC_LineControl	7
#define FC_LineStatus	8
#define FC_XmitControl	9
#define FC_FlushQueue	10
#define FC_SendBreak	11
#define FC_StopBreak	12
#define FC_LoopbackON	13
#define FC_LoopbackOFF	14
#define FC_ClrIrqTable	15
#define FC_SendXon	16
#define FC_SetTermIrq	17		/* C320 firmware not supported */
#define FC_SetCntIrq	18		/* C320 firmware not supported */
#define FC_SetBreakIrq	19
#define FC_SetLineIrq	20
#define FC_SetFlowCtl	21
#define FC_GenIrq	22
#define FC_InCD180	23
#define FC_OutCD180	24
#define FC_InUARTreg	23
#define FC_OutUARTreg	24
#define FC_SetXonXoff	25
#define FC_OutCD180CCR	26
#define FC_ExtIQueue	27
#define FC_ExtOQueue	28
#define FC_ClrLineIrq	29
#define FC_HWFlowCtl	30
#define FC_GetClockRate 35
#define FC_SetBaud	36
#define FC_SetDataMode  41
#define FC_GetCCSR      43
#define FC_GetDataError 45
#define FC_RxControl	50
#define FC_ImmSend	51
#define FC_SetXonState	52
#define FC_SetXoffState	53
#define FC_SetRxFIFOTrig 54
#define FC_SetTxFIFOCnt 55
#define FC_UnixRate	56
#define FC_UnixResetTimer 57

#define	RxFIFOTrig1	0
#define	RxFIFOTrig4	1
#define	RxFIFOTrig8	2
#define	RxFIFOTrig14	3

/*
 *	Dual-Ported RAM
 */
#define DRAM_global	0
#define INT_data	(DRAM_global + 0)
#define Config_base	(DRAM_global + 0x108)

#define IRQindex	(INT_data + 0)
#define IRQpending	(INT_data + 4)
#define IRQtable	(INT_data + 8)

/*
 *	Interrupt Status
 */
#define IntrRx		0x01		/* receiver data O.K.		*/
#define IntrTx		0x02		/* transmit buffer empty	*/
#define IntrFunc	0x04		/* function complete		*/
#define IntrBreak	0x08		/* received break		*/
#define IntrLine	0x10		/* line status change
					   for transmitter		*/
#define IntrIntr	0x20		/* received INTR code		*/
#define IntrQuit	0x40		/* received QUIT code		*/
#define IntrEOF 	0x80		/* received EOF code		*/

#define IntrRxTrigger 	0x100		/* rx data count reach tigger value*/
#define IntrTxTrigger 	0x200		/* tx data count below trigger value*/

#define Magic_no	(Config_base + 0)
#define Card_model_no	(Config_base + 2)
#define Total_ports	(Config_base + 4)
#define Module_cnt	(Config_base + 8)
#define Module_no	(Config_base + 10)
#define Timer_10ms	(Config_base + 14)
#define Disable_IRQ	(Config_base + 20)
#define TMS320_PORT1	(Config_base + 22)
#define TMS320_PORT2	(Config_base + 24)
#define TMS320_CLOCK	(Config_base + 26)

/*
 *	DATA BUFFER in DRAM
 */
#define Extern_table	0x400		/* Base address of the external table
					   (24 words *	64) total 3K bytes
					   (24 words * 128) total 6K bytes */
#define Extern_size	0x60		/* 96 bytes			*/
#define RXrptr		0x00		/* read pointer for RX buffer	*/
#define RXwptr		0x02		/* write pointer for RX buffer	*/
#define TXrptr		0x04		/* read pointer for TX buffer	*/
#define TXwptr		0x06		/* write pointer for TX buffer	*/
#define HostStat	0x08		/* IRQ flag and general flag	*/
#define FlagStat	0x0A
#define FlowControl	0x0C		/* B7 B6 B5 B4 B3 B2 B1 B0	      */
					/*  x  x  x  x	|  |  |  |	      */
					/*		|  |  |  + CTS flow   */
					/*		|  |  +--- RTS flow   */
					/*		|  +------ TX Xon/Xoff*/
					/*		+--------- RX Xon/Xoff*/
#define Break_cnt	0x0E		/* received break count 	*/
#define CD180TXirq	0x10		/* if non-0: enable TX irq	*/
#define RX_mask 	0x12
#define TX_mask 	0x14
#define Ofs_rxb 	0x16
#define Ofs_txb 	0x18
#define Page_rxb	0x1A
#define Page_txb	0x1C
#define EndPage_rxb	0x1E
#define EndPage_txb	0x20
#define Data_error	0x22
#define RxTrigger	0x28
#define TxTrigger	0x2a

#define rRXwptr 	0x34
#define Low_water	0x36

#define FuncCode	0x40
#define FuncArg 	0x42
#define FuncArg1	0x44

#define C218rx_size	0x2000		/* 8K bytes */
#define C218tx_size	0x8000		/* 32K bytes */

#define C218rx_mask	(C218rx_size - 1)
#define C218tx_mask	(C218tx_size - 1)

#define C320p8rx_size	0x2000
#define C320p8tx_size	0x8000
#define C320p8rx_mask	(C320p8rx_size - 1)
#define C320p8tx_mask	(C320p8tx_size - 1)

#define C320p16rx_size	0x2000
#define C320p16tx_size	0x4000
#define C320p16rx_mask	(C320p16rx_size - 1)
#define C320p16tx_mask	(C320p16tx_size - 1)

#define C320p24rx_size	0x2000
#define C320p24tx_size	0x2000
#define C320p24rx_mask	(C320p24rx_size - 1)
#define C320p24tx_mask	(C320p24tx_size - 1)

#define C320p32rx_size	0x1000
#define C320p32tx_size	0x1000
#define C320p32rx_mask	(C320p32rx_size - 1)
#define C320p32tx_mask	(C320p32tx_size - 1)

#define Page_size	0x2000
#define Page_mask	(Page_size - 1)
#define C218rx_spage	3
#define C218tx_spage	4
#define C218rx_pageno	1
#define C218tx_pageno	4
#define C218buf_pageno	5

#define C320p8rx_spage	3
#define C320p8tx_spage	4
#define C320p8rx_pgno	1
#define C320p8tx_pgno	4
#define C320p8buf_pgno	5

#define C320p16rx_spage 3
#define C320p16tx_spage 4
#define C320p16rx_pgno	1
#define C320p16tx_pgno	2
#define C320p16buf_pgno 3

#define C320p24rx_spage 3
#define C320p24tx_spage 4
#define C320p24rx_pgno	1
#define C320p24tx_pgno	1
#define C320p24buf_pgno 2

#define C320p32rx_spage 3
#define C320p32tx_ofs	C320p32rx_size
#define C320p32tx_spage 3
#define C320p32buf_pgno 1

/*
 *	Host Status
 */
#define WakeupRx	0x01
#define WakeupTx	0x02
#define WakeupBreak	0x08
#define WakeupLine	0x10
#define WakeupIntr	0x20
#define WakeupQuit	0x40
#define WakeupEOF	0x80		/* used in VTIME control */
#define WakeupRxTrigger	0x100
#define WakeupTxTrigger	0x200
/*
 *	Flag status
 */
#define Rx_over		0x01
#define Xoff_state	0x02
#define Tx_flowOff	0x04
#define Tx_enable	0x08
#define CTS_state	0x10
#define DSR_state	0x20
#define DCD_state	0x80
/*
 *	FlowControl
 */
#define CTS_FlowCtl	1
#define RTS_FlowCtl	2
#define Tx_FlowCtl	4
#define Rx_FlowCtl	8
#define IXM_IXANY	0x10

#define LowWater	128

#define DTR_ON		1
#define RTS_ON		2
#define CTS_ON		1
#define DSR_ON		2
#define DCD_ON		8

/* mode definition */
#define	MX_CS8		0x03
#define	MX_CS7		0x02
#define	MX_CS6		0x01
#define	MX_CS5		0x00

#define	MX_STOP1	0x00
#define	MX_STOP15	0x04
#define	MX_STOP2	0x08

#define	MX_PARNONE	0x00
#define	MX_PAREVEN	0x40
#define	MX_PARODD	0xC0

/*
 *	Query
 */
#define QueryPort	MAX_PORTS



struct	mon_str {
	int	tick;
	int	rxcnt[MAX_PORTS];
	int	txcnt[MAX_PORTS];
};
typedef struct mon_str		mon_st;

#define 	DCD_changed	0x01
#define 	DCD_oldstate	0x80

static uchar	moxaBuff[10240];
static ulong    moxaIntNdx[MAX_BOARDS];
static ulong	moxaIntPend[MAX_BOARDS];
static ulong	moxaIntTable[MAX_BOARDS];
static char	moxaChkPort[MAX_PORTS];
static char	moxaLineCtrl[MAX_PORTS];
static ulong	moxaTableAddr[MAX_PORTS];
static long	moxaCurBaud[MAX_PORTS];
static char	moxaDCDState[MAX_PORTS];
static char	moxaLowChkFlag[MAX_PORTS];
static int	moxaLowWaterChk;
static int	moxaCard;
static mon_st	moxaLog;
static int	moxaFuncTout;
static ushort	moxaBreakCnt[MAX_PORTS];

static void	moxadelay(int);
static void	moxafunc(ulong, int, ushort);
static void	wait_finish(ulong);
static void	low_water_check(ulong);
static int	moxaloadbios(int,f_uchar, int);
static int	moxafindcard(int);
static int	moxaload320b(int, f_uchar, int);
static int	moxaloadcode(int, f_uchar, int);
static int	moxaloadc218(int, ulong, int);
static int	moxaloadc320(int, ulong, int, int *);

static ulong	moxa_jiffies(void);
static int	moxa_jiffiesHz(void);
static void	moxa_memcpy_fromfs(f_uchar target, f_uchar source, int len);
static void	moxa_memcpy_tofs(f_uchar target, f_uchar source, int len);
static void	moxa_put_fs_long(int value, f_ulong *target);
static int	moxa_verify_area(int mode, void *, int len);

/*****************************************************************************
 *	Driver level functions: 					     *
 *	1. MoxaDriverInit(void);					     *
 *	2. MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port);   *
 *	3. MoxaDriverPoll(void);					     *
 *****************************************************************************/
void MoxaDriverInit(void)
{
	int	i;

	moxaFuncTout = moxa_jiffiesHz() / 2;	/* 500 mini-seconds */
	moxaCard = 0;
	moxaLog.tick = 0;
	moxaLowWaterChk = 0;
	for ( i=0; i<MAX_PORTS; i++ ) {
	    moxaChkPort[i] = 0;
	    moxaLowChkFlag[i] = 0;
	    moxaLineCtrl[i] = 0;
	    moxaLog.rxcnt[i] = 0;
	    moxaLog.txcnt[i] = 0;
	}
}

#define	MOXA		0x400
#define MOXA_GET_IQUEUE 	(MOXA + 1)	/* get input buffered count */
#define MOXA_GET_OQUEUE 	(MOXA + 2)	/* get output buffered count */
#define MOXA_INIT_DRIVER	(MOXA + 6)	/* moxaCard=0 */
#define MOXA_LOAD_BIOS		(MOXA + 9)	/* download BIOS */
#define MOXA_FIND_BOARD		(MOXA + 10)	/* Check if MOXA card exist? */
#define MOXA_LOAD_C320B		(MOXA + 11)	/* download 320B firmware */
#define MOXA_LOAD_CODE		(MOXA + 12)	/* download firmware */
#define MOXA_GETDATACOUNT       (MOXA + 23)
#define MOXA_GET_IOQUEUE	(MOXA + 27)
#define MOXA_FLUSH_QUEUE	(MOXA + 28)
#define MOXA_GET_CONF		(MOXA + 35)	/* configuration */
#define MOXA_GET_MAJOR          (MOXA + 63)
#define MOXA_GET_CUMAJOR        (MOXA + 64)
#define MOXA_GETMSTATUS         (MOXA + 65)


struct	moxaq_str {
	int	inq;
	int	outq;
};

struct dl_str {
	char	*buf;
	int	len;
	int	cardno;
};

static struct moxaq_str 	temp_queue[MAX_PORTS];
static struct dl_str		dltmp;

void MoxaPortFlushData(int port, int mode)
{
	ulong   ofsAddr;
	if ( (mode < 0)||(mode > 2))
	    return;
	ofsAddr = moxaTableAddr[port];
	moxafunc(ofsAddr, FC_FlushQueue, mode);
	if ( mode != 1 ) {
	    moxaLowChkFlag[port] = 0;
	    low_water_check(ofsAddr);
	}
}

int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port)
{
	int	i;
        int     status;
	int	MoxaPortTxQueue(int), MoxaPortRxQueue(int);

	if ( port == QueryPort ) {
	    if ( (cmd != MOXA_GET_CONF)&&(cmd != MOXA_INIT_DRIVER) &&
		 (cmd != MOXA_LOAD_BIOS) && (cmd != MOXA_FIND_BOARD) && (cmd != MOXA_LOAD_C320B) &&
		 (cmd != MOXA_LOAD_CODE) && (cmd != MOXA_GETDATACOUNT) &&
		 (cmd != MOXA_GET_IOQUEUE) && (cmd != MOXA_GET_MAJOR) &&
                 (cmd != MOXA_GET_CUMAJOR) && (cmd != MOXA_GETMSTATUS))
		return(-EINVAL);
	}
	switch ( cmd ) {
	case MOXA_GET_CONF:
	    moxa_memcpy_tofs((f_uchar)arg, (f_uchar)&moxa_boards, MAX_BOARDS*sizeof(moxa_board_conf));
	    return(0);
	case MOXA_INIT_DRIVER:
	    if ( (int)arg == 0x404 )
		MoxaDriverInit();
	    return(0);
	case MOXA_GETDATACOUNT:
	    moxaLog.tick = moxa_jiffies();
	    moxa_memcpy_tofs((f_uchar)arg, (f_uchar)&moxaLog, sizeof(mon_st));
	    return(0);
	case MOXA_FLUSH_QUEUE:
            MoxaPortFlushData(port,arg);
	    return(0);
	case MOXA_GET_IOQUEUE:
	    for ( i=0; i<MAX_PORTS; i++ ) {
		if ( moxaChkPort[i] ) {
		    temp_queue[i].inq = MoxaPortRxQueue(i);
		    temp_queue[i].outq = MoxaPortTxQueue(i);
		}
	    }
	    moxa_memcpy_tofs((f_uchar)arg, (f_uchar)temp_queue,
			     sizeof(struct moxaq_str) * MAX_PORTS);
	    return(0);
	case MOXA_LOAD_BIOS:
	    moxa_memcpy_fromfs((f_uchar)&dltmp, (f_uchar)arg,
			       sizeof(struct dl_str));
	    i = moxaloadbios(dltmp.cardno, dltmp.buf, dltmp.len);
	    return(i);
	case MOXA_FIND_BOARD:
	    moxa_memcpy_fromfs((f_uchar)&dltmp, (f_uchar)arg,
			       sizeof(struct dl_str));
	    return moxafindcard(dltmp.cardno);
	case MOXA_LOAD_C320B:
	    moxa_memcpy_fromfs((f_uchar)&dltmp, (f_uchar)arg,
			       sizeof(struct dl_str));
	    moxaload320b(dltmp.cardno, dltmp.buf, dltmp.len);
	    return(0);
	case MOXA_LOAD_CODE:
	    moxa_memcpy_fromfs((f_uchar)&dltmp, (f_uchar)arg,
			       sizeof(struct dl_str));
	    i = moxaloadcode(dltmp.cardno, dltmp.buf, dltmp.len);
	    if ( i == -1 )
		return(-EFAULT);
	    return(i);
	case MOXA_GET_OQUEUE:
	    i = moxa_verify_area(VERIFY_WRITE, (void *)arg, sizeof(long));
	    if ( i )
		return(i);
	    i = MoxaPortTxQueue(port);
	    moxa_put_fs_long(i, (f_ulong *)arg);
	    return(0);
	case MOXA_GET_IQUEUE:
	    i = moxa_verify_area(VERIFY_WRITE, (void *)arg, sizeof(long));
	    if ( i )
		return(i);
	    i = MoxaPortRxQueue(port);
	    moxa_put_fs_long(i, (f_ulong *)arg);
	    return(0);
        case MOXA_GET_MAJOR:
	    i = moxa_verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
	    if ( i )
                return(i);
	    moxa_memcpy_tofs((f_uchar)arg, (f_uchar)&ttymajor, sizeof(int));
            return 0;
        case MOXA_GET_CUMAJOR:
	    i = moxa_verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
	    if ( i )
                return(i);
	    moxa_memcpy_tofs((f_uchar)arg, (f_uchar)&calloutmajor, sizeof(int));
            return 0;
        case MOXA_GETMSTATUS:
	    i = moxa_verify_area(VERIFY_WRITE, (void *)arg,
				sizeof(struct mxser_mstatus) * MAX_PORTS);
	    if ( i )
		return(i);

            for(i=0; i<MAX_PORTS; i++){
                GMStatus[i].ri = 0;
                GMStatus[i].dcd = 0;
                GMStatus[i].dsr = 0;
                GMStatus[i].cts = 0;
		if ( !moxaChkPort[i] ) {
		    continue;
                }else{
	            status= MoxaPortLineStatus(moxaChannels[i].port);
	            if ( status & 1 )
                        GMStatus[i].cts = 1;
                    if ( status & 2 )
                        GMStatus[i].dsr = 1;
                    if ( status & 4 )
                        GMStatus[i].dcd = 1;
                }
                
	        if ( !moxaChannels[i].tty || !moxaChannels[i].tty->termios )
                    GMStatus[i].cflag = moxaChannels[i].normal_termios.c_cflag;
                else
                    GMStatus[i].cflag = moxaChannels[i].tty->termios->c_cflag;
            }
	    moxa_memcpy_tofs((f_uchar)arg, (f_uchar)GMStatus,
                sizeof(struct mxser_mstatus) * MAX_PORTS);
                
            return 0;
            
	}
	return(-ENOIOCTLCMD);
}

int MoxaDriverPoll(void)
{
	register ushort		temp;
	register int		card;
	ulong		        ip,ofsAddr;
	int			port, p, ports;

	if ( moxaCard == 0 )
	    return(-1);
	for ( card=0; card< MAX_BOARDS; card++ ) {
	    if ( (ports = moxa_boards[card].numPorts) == 0 )
		continue;
	    if ( readb(moxaIntPend[card]) == 0xff ) {
		ip = moxaIntTable[card] + readb(moxaIntNdx[card]);
		p = card * MAX_PORTS_PER_BOARD;
		ports <<= 1;
		for ( port=0; port<ports; port+= 2, p++ ) {
		    if ( (temp = readw(ip+port)) != 0 ) {
			writew(0,ip+port);
			ofsAddr = moxaTableAddr[p];
			if ( temp & IntrTx )
			    writew(readw(ofsAddr+HostStat)& ~WakeupTx,ofsAddr+HostStat);
			if ( temp & IntrBreak ) {
			    moxaBreakCnt[p]++;
			}
			if ( temp & IntrLine ) {
			    if ( readb(ofsAddr+FlagStat) & DCD_state ) {
				if ( (moxaDCDState[p] & DCD_oldstate) == 0 )
				    moxaDCDState[p] = (DCD_oldstate |
							 DCD_changed);
			    } else {
				if ( moxaDCDState[p] & DCD_oldstate )
				    moxaDCDState[p] = DCD_changed;
			    }
			}
		    }
		}
		writeb(0,moxaIntPend[card]);
	    }
	    if ( moxaLowWaterChk ) {
		p = card * MAX_PORTS_PER_BOARD;
		for ( port=0; port<ports; port++, p++ ) {
		    if ( moxaLowChkFlag[p] ) {
			moxaLowChkFlag[p] = 0;
			ofsAddr = moxaTableAddr[p];
			low_water_check(ofsAddr);
		    }
		}
	    }
	}
	moxaLowWaterChk = 0;
	return(0);
}

/*****************************************************************************
 *	Card level function:						     *
 *	1. MoxaPortsOfCard(int cardno); 				     *
 *****************************************************************************/
int  MoxaPortsOfCard(int cardno)
{

	if ( moxa_boards[cardno].boardType == 0 )
	    return(0);
	return(moxa_boards[cardno].numPorts);
}

/*****************************************************************************
 *	Port level functions:						     *
 *	1.  MoxaPortIsValid(int port);					     *
 *	2.  MoxaPortEnable(int port);					     *
 *	3.  MoxaPortDisable(int port);					     *
 *	4.  MoxaPortGetMaxBaud(int port);				     *
 *	5.  MoxaPortGetCurBaud(int port);				     *
 *	6.  MoxaPortSetBaud(int port, long baud);			     *
 *	7.  MoxaPortSetMode(int port, int databit, int stopbit, int parity); *
 *	8.  MoxaPortSetTermio(int port, unsigned char *termio); 	     *
 *	9.  MoxaPortGetLineOut(int port, int *dtrState, int *rtsState);      *
 *	10. MoxaPortLineCtrl(int port, int dtrState, int rtsState);	     *
 *	11. MoxaPortFlowCtrl(int port, int rts, int cts, int rx, int tx,int xany);    *
 *	12. MoxaPortLineStatus(int port);				     *
 *	13. MoxaPortDCDChange(int port);				     *
 *	14. MoxaPortDCDON(int port);					     *
 *	15. MoxaPortFlushData(int port, int mode);	                     *
 *	16. MoxaPortWriteData(int port, f_uchar buffer, int length);	     *
 *	17. MoxaPortReadData(int port, f_uchar buffer, int length);	     *
 *	18. MoxaPortTxBufSize(int port);				     *
 *	19. MoxaPortRxBufSize(int port);				     *
 *	20. MoxaPortTxQueue(int port);					     *
 *	21. MoxaPortTxFree(int port);					     *
 *	22. MoxaPortRxQueue(int port);					     *
 *	23. MoxaPortRxFree(int port);					     *
 *	24. MoxaPortTxDisable(int port);				     *
 *	25. MoxaPortTxEnable(int port); 				     *
 *	26. MoxaPortGetBrkCnt(int port);				     *
 *	27. MoxaPortResetBrkCnt(int port);				     *
 *	28. MoxaPortSetXonXoff(int port, int xonValue, int xoffValue);	     *
 *	29. MoxaPortIsTxHold(int port); 				     *
 *	30. MoxaPortSendBreak(int port, int ticks);			     *
 *****************************************************************************/
/*
 *	Moxa Port Number Description:
 *
 *	MOXA serial driver supports up to 4 MOXA-C218/C320 boards. And,
 *	the port number using in MOXA driver functions will be 0 to 31 for
 *	first MOXA board, 32 to 63 for second, 64 to 95 for third and 96
 *	to 127 for fourth. For example, if you setup three MOXA boards,
 *	first board is C218, second board is C320-16 and third board is
 *	C320-32. The port number of first board (C218 - 8 ports) is from
 *	0 to 7. The port number of second board (C320 - 16 ports) is form
 *	32 to 47. The port number of third board (C320 - 32 ports) is from
 *	64 to 95. And those port numbers form 8 to 31, 48 to 63 and 96 to
 *	127 will be invalid.
 *
 *
 *	Moxa Functions Description:
 *
 *	Function 1:	Driver initialization routine, this routine must be
 *			called when initialized driver.
 *	Syntax:
 *	void MoxaDriverInit();
 *
 *
 *	Function 2:	Moxa driver private IOCTL command processing.
 *	Syntax:
 *	int  MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port);
 *
 *	     unsigned int cmd	: IOCTL command
 *	     unsigned long arg	: IOCTL argument
 *	     int port		: port number (0 - 127)
 *
 *	     return:	0  (OK)
 *			-EINVAL
 *			-ENOIOCTLCMD
 *
 *
 *	Function 3:	Moxa driver polling process routine.
 *	Syntax:
 *	int  MoxaDriverPoll(void);
 *
 *	     return:	0	; polling O.K.
 *			-1	: no any Moxa card.		
 *
 *
 *	Function 4:	Get the ports of this card.
 *	Syntax:
 *	int  MoxaPortsOfCard(int cardno);
 *
 *	     int cardno 	: card number (0 - 3)
 *
 *	     return:	0	: this card is invalid
 *			8/16/24/32
 *
 *
 *	Function 5:	Check this port is valid or invalid
 *	Syntax:
 *	int  MoxaPortIsValid(int port);
 *	     int port		: port number (0 - 127, ref port description)
 *
 *	     return:	0	: this port is invalid
 *			1	: this port is valid
 *
 *
 *	Function 6:	Enable this port to start Tx/Rx data.
 *	Syntax:
 *	void MoxaPortEnable(int port);
 *	     int port		: port number (0 - 127)
 *
 *
 *	Function 7:	Disable this port
 *	Syntax:
 *	void MoxaPortDisable(int port);
 *	     int port		: port number (0 - 127)
 *
 *
 *	Function 8:	Get the maximun available baud rate of this port.
 *	Syntax:
 *	long MoxaPortGetMaxBaud(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	0	: this port is invalid
 *			38400/57600/115200 bps
 *
 *
 *	Function 9:	Get the current baud rate of this port.
 *	Syntax:
 *	long MoxaPortGetCurBaud(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	0	: this port is invalid
 *			50 - 115200 bps
 *
 *
 *	Function 10:	Setting baud rate of this port.
 *	Syntax:
 *	long MoxaPortSetBaud(int port, long baud);
 *	     int port		: port number (0 - 127)
 *	     long baud		: baud rate (50 - 115200)
 *
 *	     return:	0	: this port is invalid or baud < 50
 *			50 - 115200 : the real baud rate set to the port, if
 *				      the argument baud is large than maximun
 *				      available baud rate, the real setting
 *				      baud rate will be the maximun baud rate.
 *
 *
 *	Function 11:	Setting the data-bits/stop-bits/parity of this port
 *	Syntax:
 *	int  MoxaPortSetMode(int port, int databits, int stopbits, int parity);
 *	     int port		: port number (0 - 127)
 *	     int databits	: data bits (8/7/6/5)
 *	     int stopbits	: stop bits (2/1/0, 0 show 1.5 stop bits)
	     int parity 	: parity (0:None,1:Odd,2:Even,3:Mark,4:Space)
 *
 *	     return:	-1	: invalid parameter
 *			0	: setting O.K.
 *
 *
 *	Function 12:	Configure the port.
 *	Syntax:
 *	int  MoxaPortSetTermio(int port, struct termios *termio);
 *	     int port		: port number (0 - 127)
 *	     struct termios * termio : termio structure pointer
 *
 *	     return:	-1	: this port is invalid or termio == NULL
 *			0	: setting O.K.
 *
 *
 *	Function 13:	Get the DTR/RTS state of this port.
 *	Syntax:
 *	int  MoxaPortGetLineOut(int port, int *dtrState, int *rtsState);
 *	     int port		: port number (0 - 127)
 *	     int * dtrState	: pointer to INT to receive the current DTR
 *				  state. (if NULL, this function will not
 *				  write to this address)
 *	     int * rtsState	: pointer to INT to receive the current RTS
 *				  state. (if NULL, this function will not
 *				  write to this address)
 *
 *	     return:	-1	: this port is invalid
 *			0	: O.K.
 *
 *
 *	Function 14:	Setting the DTR/RTS output state of this port.
 *	Syntax:
 *	void MoxaPortLineCtrl(int port, int dtrState, int rtsState);
 *	     int port		: port number (0 - 127)
 *	     int dtrState	: DTR output state (0: off, 1: on)
 *	     int rtsState	: RTS output state (0: off, 1: on)
 *
 *
 *	Function 15:	Setting the flow control of this port.
 *	Syntax:
 *	void MoxaPortFlowCtrl(int port, int rtsFlow, int ctsFlow, int rxFlow,
 *			      int txFlow,int xany);
 *	     int port		: port number (0 - 127)
 *	     int rtsFlow	: H/W RTS flow control (0: no, 1: yes)
 *	     int ctsFlow	: H/W CTS flow control (0: no, 1: yes)
 *	     int rxFlow 	: S/W Rx XON/XOFF flow control (0: no, 1: yes)
 *	     int txFlow 	: S/W Tx XON/XOFF flow control (0: no, 1: yes)
 *	     int xany 		: S/W XANY flow control (0: no, 1: yes)
 *
 *
 *	Function 16:	Get ths line status of this port
 *	Syntax:
 *	int  MoxaPortLineStatus(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	Bit 0 - CTS state (0: off, 1: on)
 *			Bit 1 - DSR state (0: off, 1: on)
 *			Bit 2 - DCD state (0: off, 1: on)
 *
 *
 *	Function 17:	Check the DCD state has changed since the last read
 *			of this function.
 *	Syntax:
 *	int  MoxaPortDCDChange(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	0	: no changed
 *			1	: DCD has changed
 *
 *
 *	Function 18:	Check ths current DCD state is ON or not.
 *	Syntax:
 *	int  MoxaPortDCDON(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	0	: DCD off
 *			1	: DCD on
 *
 *
 *	Function 19:	Flush the Rx/Tx buffer data of this port.
 *	Syntax:
 *	void MoxaPortFlushData(int port, int mode);
 *	     int port		: port number (0 - 127)
 *	     int mode	 
 *			0       : flush the Rx buffer 
 *			1       : flush the Tx buffer 
 *			2       : flush the Rx and Tx buffer 
 *
 *
 *	Function 20:	Write data.
 *	Syntax:
 *	int  MoxaPortWriteData(int port, f_uchar buffer, int length);
 *	     int port		: port number (0 - 127)
 *	     uchar * buffer	: pointer to write data buffer.
 *	     int length 	: write data length
 *
 *	     return:	0 - length	: real write data length
 *
 *
 *	Function 21:	Read data.
 *	Syntax:
 *	int  MoxaPortReadData(int port, f_uchar buffer, int length);
 *	     int port		: port number (0 - 127)
 *	     uchar * buffer	: pointer to read data buffer.
 *	     int length 	: read data buffer length
 *
 *	     return:	0 - length	: real read data length
 *
 *
 *	Function 22:	Get the Tx buffer size of this port
 *	Syntax:
 *	int  MoxaPortTxBufSize(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	..	: Tx buffer size
 *
 *
 *	Function 23:	Get the Rx buffer size of this port
 *	Syntax:
 *	int  MoxaPortRxBufSize(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	..	: Rx buffer size
 *
 *
 *	Function 24:	Get the Tx buffer current queued data bytes
 *	Syntax:
 *	int  MoxaPortTxQueue(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	..	: Tx buffer current queued data bytes
 *
 *
 *	Function 25:	Get the Tx buffer current free space
 *	Syntax:
 *	int  MoxaPortTxFree(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	..	: Tx buffer current free space
 *
 *
 *	Function 26:	Get the Rx buffer current queued data bytes
 *	Syntax:
 *	int  MoxaPortRxQueue(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	..	: Rx buffer current queued data bytes
 *
 *
 *	Function 27:	Get the Rx buffer current free space
 *	Syntax:
 *	int  MoxaPortRxFree(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	..	: Rx buffer current free space
 *
 *
 *	Function 28:	Disable port data transmission.
 *	Syntax:
 *	void MoxaPortTxDisable(int port);
 *	     int port		: port number (0 - 127)
 *
 *
 *	Function 29:	Enable port data transmission.
 *	Syntax:
 *	void MoxaPortTxEnable(int port);
 *	     int port		: port number (0 - 127)
 *
 *
 *	Function 30:	Get the received BREAK signal count.
 *	Syntax:
 *	int  MoxaPortGetBrkCnt(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	0 - ..	: BREAK signal count
 *
 *
 *	Function 31:	Get the received BREAK signal count and reset it.
 *	Syntax:
 *	int  MoxaPortResetBrkCnt(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	0 - ..	: BREAK signal count
 *
 *
 *	Function 32:	Set the S/W flow control new XON/XOFF value, default
 *			XON is 0x11 & XOFF is 0x13.
 *	Syntax:
 *	void MoxaPortSetXonXoff(int port, int xonValue, int xoffValue);
 *	     int port		: port number (0 - 127)
 *	     int xonValue	: new XON value (0 - 255)
 *	     int xoffValue	: new XOFF value (0 - 255)
 *
 *
 *	Function 33:	Check this port's transmission is hold by remote site
 *			because the flow control.
 *	Syntax:
 *	int  MoxaPortIsTxHold(int port);
 *	     int port		: port number (0 - 127)
 *
 *	     return:	0	: normal
 *			1	: hold by remote site
 *
 *
 *	Function 34:	Send out a BREAK signal.
 *	Syntax:
 *	void MoxaPortSendBreak(int port, int ms100);
 *	     int port		: port number (0 - 127)
 *	     int ms100		: break signal time interval.
 *				  unit: 100 mini-second. if ms100 == 0, it will
 *				  send out a about 250 ms BREAK signal.
 *
 */
int MoxaPortIsValid(int port)
{

	if ( moxaCard == 0 )
	    return(0);
	if ( moxaChkPort[port] == 0 )
	    return(0);
	return(1);
}

void MoxaPortEnable(int port)
{
	ulong   ofsAddr;
	int	MoxaPortLineStatus(int);
	short	lowwater = 512;

	ofsAddr = moxaTableAddr[port];
	writew(lowwater,ofsAddr + Low_water);
	moxaBreakCnt[port] = 0;
	if((moxa_boards[port/MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA)||
            (moxa_boards[port/MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)){
	    moxafunc(ofsAddr, FC_SetBreakIrq, 0);
        }else{
            writew(readw(ofsAddr+HostStat)| WakeupBreak,ofsAddr+HostStat);
	}
        
	moxafunc(ofsAddr, FC_SetLineIrq, Magic_code);
	moxafunc(ofsAddr, FC_FlushQueue, 2);

	moxafunc(ofsAddr, FC_EnableCH, Magic_code);
	MoxaPortLineStatus(port);
}

void MoxaPortDisable(int port)
{
	ulong   ofsAddr;

	ofsAddr = moxaTableAddr[port];
	moxafunc(ofsAddr, FC_SetFlowCtl, 0);  /* disable flow control */
	moxafunc(ofsAddr, FC_ClrLineIrq, Magic_code);
	writew(0,ofsAddr + HostStat);
	moxafunc(ofsAddr, FC_DisableCH, Magic_code);
}

long MoxaPortGetMaxBaud(int port)
{
	if((moxa_boards[port/MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA)||
            (moxa_boards[port/MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI))
		return(460800L);
        else
		return(921600L);
}


long MoxaPortSetBaud(int port, long baud)
{
	ulong  ofsAddr;
	long	max, clock;
	uint	val;

	if ( (baud < 50L) || ((max = MoxaPortGetMaxBaud(port)) == 0) )
	    return(0);
	ofsAddr = moxaTableAddr[port];
	if ( baud > max )
	    baud = max;
	if ( max == 38400L )
	    clock = 614400L;		/* for 9.8304 Mhz : max. 38400 bps */
	else if ( max == 57600L )
	    clock = 691200L;		/* for 11.0592 Mhz : max. 57600 bps */
	else
	    clock = 921600L;		/* for 14.7456 Mhz : max. 115200 bps */
	val = clock / baud;
	moxafunc(ofsAddr, FC_SetBaud, val);
	baud = clock / val;
	moxaCurBaud[port] = baud;
	return(baud);
}

int MoxaPortSetTermio(int port, struct termios *termio)
{
	ulong   	ofsAddr;
	tcflag_t	cflag;
	long		baud;
	tcflag_t	mode = 0;

	if ( moxaChkPort[port] == 0 || termio == 0 )
	    return(-1);
	ofsAddr = moxaTableAddr[port];
	cflag = termio->c_cflag;	/* termio->c_cflag */

	mode = termio->c_cflag & CSIZE;
	if (mode == CS5)
		mode = MX_CS5;
	else if (mode == CS6)
		mode = MX_CS6;
	else if (mode == CS7) 
		mode = MX_CS7;
	else if (mode == CS8) 
		mode = MX_CS8;

	if (termio->c_cflag & CSTOPB) {
		if (mode == MX_CS5)
			mode |= MX_STOP15;
		else
			mode |= MX_STOP2;
	}
	else
		mode |= MX_STOP1;

	if (termio->c_cflag & PARENB) {
		if (termio->c_cflag & PARODD)
			mode |= MX_PARODD;
		else
			mode |= MX_PAREVEN;
	}
	else 
		mode |= MX_PARNONE;

	moxafunc(ofsAddr, FC_SetDataMode, (ushort)mode);

	cflag &= (CBAUD|CBAUDEX);
#ifndef B921600
#define	B921600	(B460800+1)
#endif
	switch ( cflag ) {
	case B921600:
		 baud = 921600L;
		 break;
	case B460800:
		 baud = 460800L;
		 break;
	case B230400:
		 baud = 230400L;
		 break;
	case B115200:
		 baud = 115200L;
		 break;
	case B57600:
		 baud = 57600L;
		 break;
	case B38400:
		 baud = 38400L;
		 break;
	case B19200:
		 baud = 19200L;
		 break;
	case B9600:
		 baud = 9600L;
		 break;
	case B4800:
		 baud = 4800L;
		 break;
	case B2400:
		 baud = 2400L;
		 break;
	case B1800:
		 baud = 1800L;
		 break;
	case B1200: 
		 baud = 1200L;
		 break;
	case B600: 
		 baud = 600L;
		 break;
	case B300:
		 baud = 300L;
		 break;
	case B200:
		 baud = 200L;
		 break;
	case B150:
		 baud = 150L;
		 break;
	case B134:
		 baud = 134L;
		 break;
	case B110:
	         baud = 110L;
		 break;
	case B75:
		 baud = 75L;
		 break;
	case B50: 
		 baud = 50L; 
		 break;
	default:
		 baud = 0;
	}
	if ( (moxa_boards[port/MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA)||
	     (moxa_boards[port/MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) {
	    if (baud == 921600L)
		return (-1);
	}
	MoxaPortSetBaud(port,baud);

	if (termio->c_iflag & (IXON | IXOFF |IXANY)) {
		writeb(termio->c_cc[VSTART],ofsAddr + FuncArg);
		writeb(termio->c_cc[VSTOP],ofsAddr + FuncArg1);
		writeb(FC_SetXonXoff,ofsAddr + FuncCode);
		wait_finish(ofsAddr);

	}
	return(0);
}

int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState)
{

	if ( !MoxaPortIsValid(port) )
	    return(-1);
	if ( dtrState ) {
	    if ( moxaLineCtrl[port] & DTR_ON )
		*dtrState = 1;
	    else
		*dtrState = 0;
	}
	if ( rtsState ) {
	    if ( moxaLineCtrl[port] & RTS_ON )
		*rtsState = 1;
	    else
		*rtsState = 0;
	}
	return(0);
}

void MoxaPortLineCtrl(int port, int dtr, int rts)
{
	ulong   ofsAddr;
	int	mode;

	ofsAddr = moxaTableAddr[port];
	mode = 0;
	if ( dtr )
	    mode |= DTR_ON;
	if ( rts )
	    mode |= RTS_ON;
	moxaLineCtrl[port] = mode;
	moxafunc(ofsAddr, FC_LineControl, mode);
}

void MoxaPortFlowCtrl(int port, int rts, int cts, int txflow, int rxflow,int txany)
{
	ulong   ofsAddr;
	int	mode;

	ofsAddr = moxaTableAddr[port];
	mode = 0;
	if ( rts )
	    mode |= RTS_FlowCtl;
	if ( cts )
	    mode |= CTS_FlowCtl;
	if ( txflow )
	    mode |= Tx_FlowCtl;
	if ( rxflow )
	    mode |= Rx_FlowCtl;
	if ( txany )
	    mode |= IXM_IXANY;
	moxafunc(ofsAddr, FC_SetFlowCtl, mode);
}

int MoxaPortLineStatus(int port)
{
	ulong   ofsAddr;
	int	val;

	ofsAddr = moxaTableAddr[port];
	if ((moxa_boards[port/MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA)||
	    (moxa_boards[port/MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) {
	   moxafunc(ofsAddr, FC_LineStatus, 0);
	   val = readw(ofsAddr + FuncArg);
	}
	else {
	   val = readw(ofsAddr + FlagStat) >> 4;
	}
        val &= 0x0B;
        if ( val & 8 ) {
           val |= 4;
           if ( (moxaDCDState[port] & DCD_oldstate) == 0 )
   	       moxaDCDState[port] = (DCD_oldstate | DCD_changed);
       } else {
           if ( moxaDCDState[port] & DCD_oldstate )
	       moxaDCDState[port] = DCD_changed;
       }
       val &= 7;
       return(val);
}

int MoxaPortDCDChange(int port)
{
	int	n;

	if ( moxaChkPort[port] == 0 )
	    return(0);
	n = moxaDCDState[port];
	moxaDCDState[port] &= ~DCD_changed;
	n &= DCD_changed;
	return(n);
}

int MoxaPortDCDON(int port)
{
	int	n;

	if ( moxaChkPort[port] == 0 )
	    return(0);
	if ( moxaDCDState[port] & DCD_oldstate )
	    n = 1;
	else
	    n = 0;
	return(n);
}


/*
int MoxaDumpMem(int port, f_uchar buffer, int len)
{
	int		i;
	ulong		baseAddr,ofsAddr,ofs;

	baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD];
	ofs = baseAddr + DynPage_addr + pageofs;
	if (len > 0x2000L)
		len = 0x2000L;
	for (i = 0; i < len; i++)
		buffer[i] = readb(ofs+i);
}
*/


int MoxaPortWriteData(int port, f_uchar buffer, int len)
{
	int		c, total, i;
	ushort		tail;
	int		cnt;
	ushort		head, tx_mask, spage, epage;
	ushort		pageno, pageofs, bufhead;
	ulong		baseAddr,ofsAddr,ofs;

	ofsAddr = moxaTableAddr[port];
	baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD];
	tx_mask = readw(ofsAddr + TX_mask);
	spage = readw(ofsAddr + Page_txb);
	epage = readw(ofsAddr + EndPage_txb);
	tail = readw(ofsAddr+TXwptr);
	head = readw(ofsAddr+TXrptr);
	c = (head > tail)  ? (head - tail - 1)
			   : (head - tail + tx_mask);
	if ( c > len )
	    c = len;
	moxaLog.txcnt[port] += c;
	total = c;
	if ( spage == epage ) {
	    bufhead = readw(ofsAddr + Ofs_txb);
	    writew(spage,baseAddr + Control_reg);
	    while ( c > 0 ) {
		if ( head > tail )
		    len = head - tail - 1;
		else
		    len = tx_mask + 1 - tail;
		len = (c > len) ? len : c;
		ofs = baseAddr + DynPage_addr + bufhead + tail;
		for ( i=0; i<len; i++ )
		    writeb(*buffer++,ofs+i);
		tail = (tail + len) & tx_mask;
		c -= len;
	    }
	    writew(tail,ofsAddr+TXwptr);
	} else {
	    len = c;
	    pageno = spage + (tail >> 13);
	    pageofs = tail & Page_mask;
	    do {
		cnt = Page_size - pageofs;
		if ( cnt > c )
		    cnt = c;
		c -= cnt;
		writeb(pageno,baseAddr + Control_reg);
		ofs = baseAddr + DynPage_addr + pageofs;
		for ( i=0; i<cnt; i++ )
		    writeb(*buffer++,ofs+i);
		if ( c == 0 ) {
	            writew((tail+len)&tx_mask,ofsAddr+TXwptr);
		    break;
		}
		if ( ++pageno == epage )
		    pageno = spage;
		pageofs = 0;
	    } while ( 1 );
	}
	writeb(1,ofsAddr + CD180TXirq);	/* start to send */
	return(total);
}

int MoxaPortReadData(int port, f_uchar buffer, int space)
{
	register ushort 	head, pageofs;
	int			i, count, cnt, len, total, remain;
	ushort			tail, rx_mask, spage, epage;
	ushort			pageno, bufhead;
	ulong			baseAddr,ofsAddr,ofs;

	ofsAddr = moxaTableAddr[port];
	baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD];
	head = readw(ofsAddr+RXrptr);
	tail = readw(ofsAddr+RXwptr);
	rx_mask = readw(ofsAddr + RX_mask);
	spage = readw(ofsAddr + Page_rxb);
	epage = readw(ofsAddr + EndPage_rxb);
	count = (tail >= head)	? (tail - head)
				: (tail - head + rx_mask + 1);
	if ( count == 0 )
	    return(0);

	total = (space > count) ? count : space;
	remain = count - total;
	moxaLog.rxcnt[port] += total;
	count = total;
	if ( spage == epage ) {
	    bufhead = readw(ofsAddr + Ofs_rxb);
	    writew(spage,baseAddr + Control_reg);
	    while ( count > 0 ) {
		if ( tail >= head )
		    len = tail - head;
		else
		    len = rx_mask + 1 - head;
		len = (count > len) ? len : count;
		ofs = baseAddr + DynPage_addr + bufhead + head;
		for ( i=0; i<len; i++ )
		    *buffer++=readb(ofs+i);
		head = (head + len) & rx_mask;
		count -= len;
	    }
	    writew(head,ofsAddr + RXrptr);
	} else {
	    len = count;
	    pageno = spage + (head >> 13);
	    pageofs = head & Page_mask;
	    do {
		cnt = Page_size - pageofs;
		if ( cnt > count )
		    cnt = count;
		count -= cnt;
		writew(pageno,baseAddr + Control_reg);
		ofs = baseAddr + DynPage_addr + pageofs;
		for ( i=0; i<cnt; i++ )
		    *buffer++ = readb(ofs+i);
		if ( count == 0 ) {
	            writew((head+len)&rx_mask,ofsAddr + RXrptr);
		    break;
		}
		if ( ++pageno == epage )
		    pageno = spage;
		pageofs = 0;
	    } while ( 1 );
	}
	if ((readb(ofsAddr + FlagStat) & Xoff_state) && (remain < LowWater) ) {
	    moxaLowWaterChk = 1;
	    moxaLowChkFlag[port] = 1;
	}
	return(total);
}


int MoxaPortTxQueue(int port)
{
	ulong  ofsAddr;
	ushort	rptr, wptr, mask;
	int	len;

	ofsAddr = moxaTableAddr[port];
	rptr = readw(ofsAddr + TXrptr);
	wptr = readw(ofsAddr + TXwptr);
	mask = readw(ofsAddr + TX_mask);
	len = (wptr - rptr) & mask;
	return(len);
}

int MoxaPortTxFree(int port)
{
	ulong  ofsAddr;
	ushort	rptr, wptr, mask;
	int	len;

	ofsAddr = moxaTableAddr[port];
	rptr = readw(ofsAddr + TXrptr);
	wptr = readw(ofsAddr + TXwptr);
	mask = readw(ofsAddr + TX_mask);
	len = mask - ((wptr - rptr) & mask);
	return(len);
}

int MoxaPortRxQueue(int port)
{
	ulong  ofsAddr;
	ushort	rptr, wptr, mask;
	int	len;

	ofsAddr = moxaTableAddr[port];
	rptr = readw(ofsAddr + RXrptr);
	wptr = readw(ofsAddr + RXwptr);
	mask = readw(ofsAddr + RX_mask);
	len = (wptr - rptr) & mask;
	return(len);
}


void MoxaPortTxDisable(int port)
{
	ulong  ofsAddr;

	ofsAddr = moxaTableAddr[port];
	moxafunc(ofsAddr, FC_SetXoffState, Magic_code);
}

void MoxaPortTxEnable(int port)
{
	ulong   ofsAddr;

	ofsAddr = moxaTableAddr[port];
	moxafunc(ofsAddr, FC_SetXonState, Magic_code);
}


int MoxaPortResetBrkCnt(int port)
{
	ushort	cnt;
	cnt = moxaBreakCnt[port];
	moxaBreakCnt[port] = 0;
	return (cnt);	
}


void MoxaPortSendBreak(int port, int ms100)
{
	ulong   ofsAddr;

	ofsAddr = moxaTableAddr[port];
	if ( ms100 ) {
	    moxafunc(ofsAddr, FC_SendBreak, Magic_code);
	    moxadelay(ms100 * (moxa_jiffiesHz() / 10));
	} else {
	    moxafunc(ofsAddr, FC_SendBreak, Magic_code);
	    moxadelay(moxa_jiffiesHz() / 4);	/* 250 ms */
	}
	moxafunc(ofsAddr, FC_StopBreak,Magic_code);
}

static int moxa_get_serial_info(struct moxa_str * info,
				 struct serial_struct * retinfo)
{
	struct serial_struct	tmp;

	if ( !retinfo )
	    return(-EFAULT);
	memset(&tmp, 0, sizeof(tmp));
	tmp.type = info->type;
	tmp.line = info->port;
	tmp.port = 0;
	tmp.irq = 0;
	tmp.flags = info->asyncflags;
	tmp.baud_base = 921600;
	tmp.close_delay = info->close_delay;
	tmp.closing_wait = info->closing_wait;
	tmp.custom_divisor = 0;
	tmp.hub6 = 0;
	copy_to_user(retinfo, &tmp, sizeof(*retinfo));
	return(0);
}


static int moxa_set_serial_info(struct moxa_str * info,
				 struct serial_struct * new_info)
{
	struct serial_struct	new_serial;

	if ( !new_info )
	    return(-EFAULT);
	copy_from_user(&new_serial, new_info, sizeof(new_serial));

	if ( (new_serial.irq != 0) ||
	     (new_serial.port != 0) ||
//	     (new_serial.type != info->type) ||
	     (new_serial.custom_divisor != 0) ||
	     (new_serial.baud_base != 921600) )
	    return(-EPERM);

	if ( !suser() ) {
	    if ( ((new_serial.flags & ~ASYNC_USR_MASK) !=
		 (info->asyncflags & ~ASYNC_USR_MASK)) )
		return(-EPERM);
	} else {
	    info->close_delay = new_serial.close_delay * HZ/100;
	    info->closing_wait = new_serial.closing_wait * HZ/100;
	}

        new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS);
        new_serial.flags |= (info->asyncflags & ASYNC_FLAGS);

	if(new_serial.type == PORT_16550A){
		MoxaSetFifo(info->port, 1);
	}else{
		MoxaSetFifo(info->port, 0);
	}

        info->type = new_serial.type;
	return(0);
}
            


/*****************************************************************************
 *	Static local functions: 					     *
 *****************************************************************************/
/*
 * moxadelay - delays a specified number ticks
 */
static void moxadelay(int tick)
{
	ulong	st, et;

	st = moxa_jiffies();
	et = st + tick;
	while ( moxa_jiffies() < et );
}

static void moxafunc(ulong ofsAddr, int cmd, ushort arg)
{

	writew(arg,ofsAddr + FuncArg);
	writew(cmd,ofsAddr + FuncCode);
	wait_finish(ofsAddr);
}

static void wait_finish(ulong ofsAddr)
{
	ulong	i, j;

	i = moxa_jiffies();
	while ( readw(ofsAddr + FuncCode) != 0 ) {
	    j = moxa_jiffies();
	    if ( (j - i) > moxaFuncTout )  {
		return;
	     }
	}
}

static void low_water_check(ulong ofsAddr)
{
	int	len;
	ushort	rptr, wptr, mask;

	if ( readb(ofsAddr + FlagStat) & Xoff_state ) {
	    rptr = readw(ofsAddr + RXrptr);
	    wptr = readw(ofsAddr + RXwptr);
	    mask = readw(ofsAddr + RX_mask);
	    len = (wptr - rptr) & mask;
	    if ( len <= Low_water )
		moxafunc(ofsAddr, FC_SendXon, 0);
	}
}

static int moxaloadbios(int cardno, f_uchar tmp, int len)
{
	ulong   baseAddr;
	int	i;

	moxa_memcpy_fromfs(moxaBuff, tmp, len);
	baseAddr = moxaBaseAddr[cardno];
        writeb(HW_reset,baseAddr+Control_reg);/* reset */
        moxadelay(1);			     /* delay 10 ms */
        for ( i=0; i<4096; i++ )
	   writeb(0,baseAddr+i);	     /* clear fix page */
        for ( i=0; i<len; i++ )
	   writeb(moxaBuff[i],baseAddr+i);    /* download BIOS */
        writeb(0,baseAddr+Control_reg);    /* restart */
        return(0);
}

static int moxafindcard(int cardno)
{
	ulong baseAddr;
	ushort tmp;

	baseAddr = moxaBaseAddr[cardno];
	switch ( moxa_boards[cardno].boardType ) {
	case MOXA_BOARD_C218_ISA:
	case MOXA_BOARD_C218_PCI:
	    if ( (tmp=readw(baseAddr + C218_key)) != C218_KeyCode ) {
		return(-1);
	    }
	    break;
	case MOXA_BOARD_CP204J:
	    if ( (tmp=readw(baseAddr + C218_key)) != CP204J_KeyCode ) {
		return(-1);
	    }
	    break;
	default:
	    if ( (tmp=readw(baseAddr + C320_key)) != C320_KeyCode ) {
		return(-1);
	    }
	    if ( (tmp=readw(baseAddr + C320_status)) != STS_init ) {
		return(-2);
	    }
	}
	return(0);
}

static int moxaload320b(int cardno, f_uchar tmp, int len)
{
	ulong   baseAddr;
	int	i;

	moxa_memcpy_fromfs(moxaBuff, tmp, len);
	baseAddr = moxaBaseAddr[cardno];
	writew(len-7168-2,baseAddr+C320bapi_len);
	writeb(1,baseAddr+Control_reg);		/* Select Page 1 */
	for ( i=0; i<7168; i++ )
	    writeb(moxaBuff[i],baseAddr+DynPage_addr + i);
	writeb(2,baseAddr+Control_reg);		/* Select Page 2 */
	for ( i=0; i<(len - 7168); i++ )
	    writeb(moxaBuff[i+7168],baseAddr+DynPage_addr + i);
	return(0);
}

static int moxaloadcode(int cardno, f_uchar tmp, int len)
{
	ulong   baseAddr, ofsAddr;
	int	retval, port, i;

	moxa_memcpy_fromfs(moxaBuff, tmp, len);
	baseAddr = moxaBaseAddr[cardno];
	switch ( moxa_boards[cardno].boardType ) {
	case MOXA_BOARD_C218_ISA:
	case MOXA_BOARD_C218_PCI:
	case MOXA_BOARD_CP204J:
	    retval = moxaloadc218(cardno, baseAddr, len);
	    if ( retval )
		return(retval);
	    port = cardno * MAX_PORTS_PER_BOARD;
	    for ( i=0; i< moxa_boards[cardno].numPorts; i++, port++ ) {	
		moxaChkPort[port] = 1;
		moxaCurBaud[port] = 9600L;
		moxaDCDState[port] = 0;
		moxaTableAddr[port] = baseAddr + Extern_table + Extern_size * i;
		ofsAddr = moxaTableAddr[port];
		writew(C218rx_mask,ofsAddr + RX_mask);
		writew(C218tx_mask,ofsAddr + TX_mask);
		writew(C218rx_spage + i * C218buf_pageno,ofsAddr + Page_rxb); 
		writew(readw(ofsAddr + Page_rxb) + C218rx_pageno,ofsAddr + EndPage_rxb);
						
		writew(C218tx_spage + i * C218buf_pageno,ofsAddr + Page_txb); 
		writew(readw(ofsAddr + Page_txb)+C218tx_pageno,ofsAddr + EndPage_txb);
						 
	    }
	    break;
	default :
	    retval = moxaloadc320(cardno, baseAddr, len,
					 &moxa_boards[cardno].numPorts);
	    if ( retval )
		return(retval);
	    port = cardno * MAX_PORTS_PER_BOARD;
	    for ( i=0; i< moxa_boards[cardno].numPorts; i++, port++ ) {	
		moxaChkPort[port] = 1;
		moxaCurBaud[port] = 9600L;
		moxaDCDState[port] = 0;
		moxaTableAddr[port] = baseAddr + Extern_table + Extern_size * i;
		ofsAddr= moxaTableAddr[port];
		if (moxa_boards[cardno].numPorts == 8) {
		    writew(C320p8rx_mask,ofsAddr + RX_mask);
		    writew(C320p8tx_mask,ofsAddr + TX_mask);
		    writew(C320p8rx_spage + i*C320p8buf_pgno,ofsAddr + Page_rxb);
		    writew(readw(ofsAddr + Page_rxb)+C320p8rx_pgno,ofsAddr + EndPage_rxb);
		    writew(C320p8tx_spage + i*C320p8buf_pgno,ofsAddr + Page_txb);
		    writew( readw(ofsAddr + Page_txb)+C320p8tx_pgno,ofsAddr + EndPage_txb);
					
	    	}
		else if (moxa_boards[cardno].numPorts == 16) {
		    writew(C320p16rx_mask,ofsAddr + RX_mask);
		    writew(C320p16tx_mask,ofsAddr + TX_mask);
		    writew(C320p16rx_spage+i * C320p16buf_pgno,ofsAddr + Page_rxb); 
		    writew(readw(ofsAddr + Page_rxb)+C320p16rx_pgno,ofsAddr + EndPage_rxb); 
		    writew(C320p16tx_spage +i * C320p16buf_pgno,ofsAddr + Page_txb);
		    writew(readw(ofsAddr + Page_txb) +C320p16tx_pgno,ofsAddr + EndPage_txb); 
						 
	        }
		else if (moxa_boards[cardno].numPorts == 24) {
		    writew(C320p24rx_mask,ofsAddr + RX_mask);
		    writew(C320p24tx_mask,ofsAddr + TX_mask);
		    writew(C320p24rx_spage +i * C320p24buf_pgno,ofsAddr + Page_rxb);
		    writew(readw(ofsAddr + Page_rxb)+C320p24rx_pgno,ofsAddr + EndPage_rxb); 
		    writew( C320p24tx_spage+ i* C320p24buf_pgno,ofsAddr + Page_txb);
		    writew(readw(ofsAddr + Page_txb),ofsAddr + EndPage_txb); 
	        }
		else if (moxa_boards[cardno].numPorts == 32) {
		    writew(C320p32rx_mask,ofsAddr + RX_mask);
		    writew(C320p32tx_mask,ofsAddr + TX_mask);
		    writew(C320p32tx_ofs,ofsAddr + Ofs_txb);
		    writew(C320p32rx_spage+ i* C320p32buf_pgno,ofsAddr + Page_rxb); 
		    writew(readb(ofsAddr + Page_rxb),ofsAddr + EndPage_rxb);
	            writew(C320p32tx_spage+ i* C320p32buf_pgno,ofsAddr + Page_txb); 
		    writew(readw(ofsAddr + Page_txb),ofsAddr + EndPage_txb); 
	       }
	    }
	    break;
	}
	return(0);
}

static int moxaloadc218(int cardno, ulong baseAddr, int len)
{
	char	retry;
	int	i,j,len1,len2;
	ushort	usum,*ptr,keycode;

	if (moxa_boards[cardno].boardType == MOXA_BOARD_CP204J)
	    keycode = CP204J_KeyCode;
	else
	    keycode = C218_KeyCode;
	usum = 0;
	len1 = len >> 1;
	ptr = (ushort *)moxaBuff;
	for ( i=0; i<len1; i++ )
	    usum += *(ptr+i);
	retry = 0;
	do {
	     len1 = len >> 1;
	     j = 0;
	     while (len1) {
	         len2 = (len1 > 2048) ? 2048 : len1;
		 len1 -= len2;
		 for (i=0; i<len2<<1; i++)
		     writeb(moxaBuff[i+j],baseAddr+C218_LoadBuf+i);
		 j += i;	

	         writew(len2,baseAddr+C218DLoad_len);
	         writew(0,baseAddr+C218_key);
	         for ( i=0; i<100; i++ ) {
		     if ( readw(baseAddr+C218_key) == keycode )
		         break;
		     moxadelay(1);		    /* delay 10 ms */
		 }
	         if ( readw(baseAddr+C218_key) != keycode ) {
		     return(-1);
	         }
	    }
	    writew(0,baseAddr+C218DLoad_len);
	    writew(usum,baseAddr+C218check_sum);
	    writew(0,baseAddr+C218_key);
	    for ( i=0; i<100; i++ ) {
		if (readw(baseAddr+C218_key) == keycode )
		    break;
		moxadelay(1);		    /* delay 10 ms */
	    }
	    retry++;
	} while ( (readb(baseAddr+C218chksum_ok) != 1) && (retry < 3) );
	if ( readb(baseAddr+C218chksum_ok) != 1 ) {
	    return(-1);
	}
	writew(0,baseAddr+C218_key);
	for ( i=0; i<100; i++ ) {
	    if ( readw(baseAddr+Magic_no) == Magic_code )
		break;
	    moxadelay(1);		/* delay 10 ms */
	}
	if ( readw(baseAddr+Magic_no) != Magic_code ) {
	    return(-1);
	}

	writew(1,baseAddr+Disable_IRQ);
	writew(0,baseAddr+Magic_no);
	for ( i=0; i<100; i++ ) {
	    if ( readw(baseAddr+Magic_no) == Magic_code )
		break;
	    moxadelay(1);		/* delay 10 ms */
	}
	if ( readw(baseAddr+Magic_no) != Magic_code ) {
	    return(-1);
	}

	moxaCard = 1;
	moxaIntNdx[cardno] = baseAddr + IRQindex;
	moxaIntPend[cardno] = baseAddr + IRQpending;
	moxaIntTable[cardno] = baseAddr + IRQtable;
	return(0);
}

static int moxaloadc320(int cardno, ulong baseAddr, int len, int *numPorts)
{
	ushort	usum;
	int	i, j, wlen, len2, retry;
	ushort	*uptr;

	usum = 0;
	wlen = len >> 1;
	uptr = (ushort *)moxaBuff;
	for ( i=0; i<wlen; i++ )
	    usum += uptr[i];
	retry = 0;
	j = 0;
	do {
	    while ( wlen ) {
		if ( wlen > 2048 )
		    len2 = 2048;
		else
		    len2 = wlen;
		wlen -= len2;
		len2 <<= 1;
		for ( i=0; i<len2; i++ )
		    writeb(moxaBuff[j+i],baseAddr+C320_LoadBuf+i);
		len2 >>= 1;
		j += i;
		writew(len2,baseAddr+C320DLoad_len);
		writew(0,baseAddr+C320_key);
		for ( i=0; i<10; i++ ) {
		    if ( readw(baseAddr+C320_key) == C320_KeyCode )
			break;
		    moxadelay(1);
		}
		if ( readw(baseAddr+C320_key) != C320_KeyCode )
		    return(-1);
	    }
	    writew(0,baseAddr+C320DLoad_len);
	    writew(usum,baseAddr+C320check_sum);
	    writew(0,baseAddr+C320_key);
	    for ( i=0; i<10; i++ ) {
		if ( readw(baseAddr+C320_key) == C320_KeyCode )
		   break;
		moxadelay(1);
	    }
	    retry++;
	} while ( (readb(baseAddr+C320chksum_ok) != 1) && (retry < 3) );
	if ( readb(baseAddr+C320chksum_ok) != 1 )
	    return(-1);
	writew(0,baseAddr+C320_key);
	for ( i=0; i<600; i++ ) {
	    if ( readw(baseAddr+Magic_no) == Magic_code )
		break;
	    moxadelay(1);
	}
	if ( readw(baseAddr+Magic_no) != Magic_code )
	    return(-100);

	if (moxa_boards[cardno].busType == MOXA_BUS_TYPE_PCI) { /* ASIC board */
		writew(0x3800,baseAddr+TMS320_PORT1);
		writew(0x3900,baseAddr+TMS320_PORT2);
		writew(28499,baseAddr+TMS320_CLOCK);
	}
	else {
		writew(0x3200,baseAddr+TMS320_PORT1);
		writew(0x3400,baseAddr+TMS320_PORT2);
		writew(19999,baseAddr+TMS320_CLOCK);
	}
	writew(1,baseAddr+Disable_IRQ);
	writew(0,baseAddr+Magic_no);
	for (i=0; i<500; i++) {
	    if ( readw(baseAddr+Magic_no) == Magic_code )
		   break;
             moxadelay(1);
	}
	if ( readw(baseAddr+Magic_no) != Magic_code )
	       return(-102);

	j = readw(baseAddr+Module_cnt);
	if (j <= 0)
	    return(-101);
	*numPorts = j*8;
	writew(j,baseAddr+Module_no);
	writew(0,baseAddr+Magic_no);
	for ( i=0; i<600; i++ ) {
	    if ( readw(baseAddr+Magic_no) == Magic_code )
		break;
	    moxadelay(1);
	}
	if ( readw(baseAddr+Magic_no) != Magic_code )
	    return(-102);
	moxaCard = 1;
	moxaIntNdx[cardno] = baseAddr + IRQindex;
	moxaIntPend[cardno] = baseAddr + IRQpending;
	moxaIntTable[cardno] = baseAddr + IRQtable;
	return(0);
}

long MoxaPortGetCurBaud(int port)
{

	if ( moxaChkPort[port] == 0 )
	    return(0);
	return( moxaCurBaud[port] );
}

static void MoxaSetFifo(int port, int enable)
{
	ulong ofsAddr = moxaTableAddr[port];

        if ( !enable ){
	    moxafunc(ofsAddr, FC_SetRxFIFOTrig, 0);
	    moxafunc(ofsAddr, FC_SetTxFIFOCnt, 1);
        }else{
	    moxafunc(ofsAddr, FC_SetRxFIFOTrig, 3);
	    moxafunc(ofsAddr, FC_SetTxFIFOCnt, 16);
        }
}

#if 0
int MoxaPortSetMode(int port, int databits, int stopbits, int parity)
{
	ulong   ofsAddr;
	int	val;

	val = 0;
	switch ( databits ) {
	case 5: val |= 0; break;
	case 6: val |= 1; break;
	case 7: val |= 2; break;
	case 8: val |= 3; break;
	default:
	    return(-1);
	}
	switch ( stopbits ) {
	case 0: val |= 0; break;	/* stop bits 1.5 */
	case 1: val |= 0; break;
	case 2: val |= 4; break;
	default:
	    return(-1);
	}
	switch ( parity ) {
	case 0: val |= 0x00; break;	/* None  */
	case 1: val |= 0x08; break;	/* Odd	 */
	case 2: val |= 0x18; break;	/* Even  */
	case 3: val |= 0x28; break;	/* Mark  */
	case 4: val |= 0x38; break;	/* Space */
	default:
	    return(-1);
	}
	ofsAddr = moxaTableAddr[port];
	moxafunc(ofsAddr, FC_SetMode, val);
	return(0);
}

int MoxaPortTxBufSize(int port)
{
	ulong  ofsAddr;
	int	size;

	ofsAddr = moxaTableAddr[port];
	size = readw(ofsAddr + TX_mask);
	return(size);
}

int MoxaPortRxBufSize(int port)
{
	ulong  ofsAddr;
	int	size;

	ofsAddr = moxaTableAddr[port];
	size = readw(ofsAddr + RX_mask);
	return(size);
}

int MoxaPortRxFree(int port)
{
	ulong  ofsAddr;
	ushort	rptr, wptr, mask;
	int	len;

	ofsAddr = moxaTableAddr[port];
	rptr = readw(ofsAddr + RXrptr);
	wptr = readw(ofsAddr + RXwptr);
	mask = readw(ofsAddr + RX_mask);
	len = mask - ((wptr - rptr) & mask);
	return(len);
}
int MoxaPortGetBrkCnt(int port)
{
	return (moxaBreakCnt[port]);
}

void MoxaPortSetXonXoff(int port, int xonValue, int xoffValue)
{
	ulong   ofsAddr;

	ofsAddr = moxaTableAddr[port];
	writew(xonValue,ofsAddr + FuncArg);
	writew(xoffValue,ofsAddr + FuncArg1);
	writew(FC_SetXonXoff,ofsAddr + FuncCode);
	wait_finish(ofsAddr);
}

int MoxaPortIsTxHold(int port)
{
	ulong   ofsAddr;
	int	val;

	ofsAddr = moxaTableAddr[port];
	if ((moxa_boards[port/MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA)||
	    (moxa_boards[port/MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI) ){
	    moxafunc(ofsAddr, FC_GetCCSR, 0);
	    val = readw(ofsAddr + FuncArg);
	    if ( val & 0x04 )
	        return(1);
	}
	else {
	   if (readw(ofsAddr+FlagStat) & Tx_flowOff)
		return (1);	
	}
	return(0);
}
#endif
