
/*****************************************************************************/
/*
 *          srllinux.c  -- ACKSYS family multiport serial driver.
 *
 *
 *      This code is loosely based on the Moxa smartio family serial driver
 *
 *      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.
 */

/*
 *	ACKSYS Family Serial Driver
 *
 *
 *	for		: LINUX 2.0.X, 2.2.X
 *	date		: 2001/04/3
 *	version 	: 1.1
 *	
 */
#define _LOOSE_KERNEL_NAMES

#ifdef 		MODVERSIONS
#ifndef 	MODULE
#define 	MODULE
#endif
#endif

#ifdef MODULE
#include <linux/config.h>
#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/version.h>
#include <linux/autoconf.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/serial.h>
#include <linux/serial_reg.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/smp_lock.h>

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

#define		VERSION_CODE(ver,rel,seq)	((ver << 16) | (rel << 8) | seq)
#ifdef CONFIG_PCI
#include <linux/pci.h>
#endif

#define		SRLLINUX_VERSION			"2.1"

#define		MXSERMAJOR	 	40
//#define		MXSERCUMAJOR		45

#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 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 

#define	MXSER_EVENT_TXLOW	 1
#define	MXSER_EVENT_HANGUP	 2


#define 	SERIAL_DO_RESTART

#define 	MXSER_BOARDS		8	/* Max. boards */

#define 	MXSER_PORTS_PER_BOARD	8	/* Max. ports per board*/

#define 	MXSER_PORTS	(MXSER_BOARDS*MXSER_PORTS_PER_BOARD) /* Max. ports*/
#define 	MXSER_ISR_PASS_LIMIT	256

#define		MXSER_ERR_IOADDR	-1
#define		MXSER_ERR_IRQ		-2
#define		MXSER_ERR_IRQ_CONFLIT	-3
#define		MXSER_ERR_VECTOR	-4

#define 	SERIAL_TYPE_NORMAL	1
//#define 	SERIAL_TYPE_CALLOUT	2

#define 	WAKEUP_CHARS		256

#define 	UART_MCR_AFE		0x20
#define 	UART_LSR_SPECIAL	0x1E

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

#define RELEVANT_IFLAG(iflag)	(iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))

#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)

#define _INLINE_	inline

#ifndef MIN
#define MIN(a,b)	((a) < (b) ? (a) : (b))
#endif

#include "uart.h"
#include "iocontrol.h"
#define		PORT_OX954			PORT_MAX+1
/*
 *	Define the ACKSYS PCI vendor and device IDs.
 */
#ifndef	PCI_VENDOR_ID_ACKSYS
#define	PCI_VENDOR_ID_ACKSYS	0x1528
#endif

#ifndef PCI_DEVICE_ID_XRSPCI_2
#define PCI_DEVICE_ID_XRSPCI_2	0x12
#endif

#ifndef PCI_DEVICE_ID_XRSPCI_4
#define PCI_DEVICE_ID_XRSPCI_4	0x10
#endif

#ifndef PCI_DEVICE_ID_EXT_XRSPCI_4
#define PCI_DEVICE_ID_EXT_XRSPCI_4 0x11
#endif

#ifndef PCI_DEVICE_ID_UNXPCI
#define PCI_DEVICE_ID_UNXPCI 0x01
#endif

enum	{
	MXSER_BOARD_XRSPCI_2=1,
	MXSER_BOARD_XRSPCI_4,
	MXSER_BOARD_EXT_XRSPCI_4,
	MXSER_BOARD_UNXPCI_4,
	MXSER_BOARD_UNXPCI_8,
};

static char *mxser_brdname[] = {
	"XRSPCI 2 channels",
	"XRSPCI 4 channels",
    "XRSPCI 8 channels",
	"UNXPCI 4 channels",
	"UNXPCI 8 channels",
};

static int mxser_numports[] = {
	2,
	4,
   	4,
	4,
	8,
};

/*
 *	ACKSYS ioctls
 */
/*
#define 	ACKSYS		0x400
#define 	ACKSYS_GETDATACOUNT     (ACKSYS + 23)
#define		ACKSYS_GET_CONF         (ACKSYS + 35)
#define 	ACKSYS_DIAGNOSE         (ACKSYS + 50)
#define 	ACKSYS_CHKPORTENABLE    (ACKSYS + 60)
#define 	ACKSYS_HighSpeedOn      (ACKSYS + 61)
#define     ACKSYS_GET_MAJOR        (ACKSYS + 63)
#define     ACKSYS_GET_CUMAJOR      (ACKSYS + 64)
#define     ACKSYS_GETMSTATUS       (ACKSYS + 65)
#define     ACKSYS_READ_REGISTRE    (ACKSYS + 66)
#define 	ACKSYS_ENABLE_485_MODE  (ACKSYS + 67)
#define     ACKSYS_DISABLE_485_MODE (ACKSYS + 68)
*/
#ifdef CONFIG_PCI

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

static mxser_pciinfo	mxser_pcibrds[] = {
	{PCI_VENDOR_ID_ACKSYS,PCI_DEVICE_ID_XRSPCI_2 ,MXSER_BOARD_XRSPCI_2},
	{PCI_VENDOR_ID_ACKSYS,PCI_DEVICE_ID_XRSPCI_4 ,MXSER_BOARD_XRSPCI_4},
	{PCI_VENDOR_ID_ACKSYS,PCI_DEVICE_ID_UNXPCI,MXSER_BOARD_UNXPCI_4},
};

typedef struct _acksys_pci_info {
	unsigned short busNum;
	unsigned short devNum;
} acksys_pci_info;

#endif

static int ttymajor=MXSERMAJOR;
//static int calloutmajor=MXSERCUMAJOR;
static int verbose=0;
static int input_clock = 29491200;
static char device_name[] ="ttyA";
//static char cdevice_name[] = "cumA";
#ifdef MODULE
/* Variables for insmod */

# if (LINUX_VERSION_CODE > VERSION_CODE(2,1,11))
MODULE_AUTHOR("Voncken cedric");
MODULE_DESCRIPTION("ACKSYS XRS PCI Family Multiport Board Device Driver");
MODULE_PARM(ttymajor,        "i");
//MODULE_PARM(calloutmajor,    "i");
MODULE_PARM(verbose,        "i");
MODULE_PARM(input_clock,    "i");
# endif

#endif MODULE

struct mxser_hwconf {
	int			board_type;
	int			ports;
	int			irq;
	int			vector;
	int			vector_mask;
	int			uart_type;
	int			ioaddr[MXSER_PORTS_PER_BOARD];
	int			baud_base[MXSER_PORTS_PER_BOARD];
	acksys_pci_info		pciInfo;
};

struct mxser_struct {
	int			port;
	int			base;		/* port base address */
	int			irq;		/* port using irq no. */
	int			vector; 	/* port irq vector */
	int			vectormask;	/* port vector mask */
	int			rx_trigger;	/* Rx fifo trigger level */
	int			baud_base;	/* max. speed */
	int			flags;		/* defined in tty.h */
	int			type;		/* UART type */
	struct tty_struct *	tty;
	int			read_status_mask;
	int			ignore_status_mask;
	int			xmit_fifo_size;
	int			custom_divisor;
	int			x_char; 	/* xon/xoff character */
	int			close_delay;
	unsigned short		closing_wait;
	int			IER;		/* Interrupt Enable Register */
	int			MCR;		/* Modem control register */
	int 		ACR;		/* Advanced control register*/
	unsigned long		event;
	int			count;		/* # of fd on device */
	int			blocked_open;	/* # of blocked opens */
	long			session;	/* Session of opening process */
	long			pgrp;		/* pgrp of opening process */
	unsigned char		*xmit_buf;
	int			xmit_head;
	int			xmit_tail;
	int			xmit_cnt;
	struct tq_struct	tqueue;
	struct termios		normal_termios;
	struct termios		callout_termios;
	struct wait_queue	*open_wait;
	struct wait_queue	*close_wait;
	struct wait_queue	*delta_msr_wait;
	struct async_icount	icount; 	/* kernel counters for the 4 input interrupts */
};

struct mxser_log {
	int	tick;
	int	rxcnt[MXSER_PORTS];
	int	txcnt[MXSER_PORTS];
};

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

static  struct mxser_mstatus GMStatus[MXSER_PORTS];

static struct tty_driver	mxvar_sdriver, mxvar_cdriver;
static int			mxvar_refcount;
static struct mxser_struct	mxvar_table[MXSER_PORTS];
static struct tty_struct *	mxvar_tty[MXSER_PORTS+1];
static struct termios * 	mxvar_termios[MXSER_PORTS+1];
static struct termios * 	mxvar_termios_locked[MXSER_PORTS+1];
static struct mxser_log 	mxvar_log;
static int			mxvar_diagflag;
/*
 * mxvar_tmp_buf is used as a temporary buffer by serial_write. We need
 * to lock it in case the memcpy_fromfs blocks while swapping in a page,
 * and some other program tries to do a serial write at the same time.
 * Since the lock will only come under contention when the system is
 * swapping and available memory is low, it makes sense to share one
 * buffer across all the serial ports, since it significantly saves
 * memory if large numbers of serial ports are open.
 */
static unsigned char *		mxvar_tmp_buf = 0;
static struct semaphore 	mxvar_tmp_buf_sem = MUTEX;

/*
 * This is used to figure out the divisor speeds and the timeouts
 */
static int mxvar_baud_table[] = {
	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
	9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 0 };
	
struct mxser_hwconf mxsercfg[MXSER_BOARDS];

/*
 * static functions:
 */

#ifdef MODULE
int		init_module(void);
void		cleanup_module(void);
#endif

static void 	mxser_getcfg(int board,struct mxser_hwconf *hwconf);
int		mxser_init(void);
static int      mxser_get_PCI_conf(int ,int ,int ,struct mxser_hwconf *);
static void	mxser_do_softint(void *);
static int	mxser_open(struct tty_struct *, struct file *);
static void	mxser_close(struct tty_struct *, struct file *);
static int	mxser_write(struct tty_struct *, int, const unsigned char *, int);
static int	mxser_write_room(struct tty_struct *);
static void	mxser_flush_buffer(struct tty_struct *);
static int	mxser_chars_in_buffer(struct tty_struct *);
static void	mxser_flush_chars(struct tty_struct *);
static void	mxser_put_char(struct tty_struct *, unsigned char);
static int	mxser_ioctl(struct tty_struct *, struct file *, uint, ulong);
static int	mxser_ioctl_special(unsigned int, unsigned long);
static void	mxser_throttle(struct tty_struct *);
static void	mxser_unthrottle(struct tty_struct *);
static void	mxser_set_termios(struct tty_struct *, struct termios *);
static void	mxser_stop(struct tty_struct *);
static void	mxser_start(struct tty_struct *);
static void	mxser_hangup(struct tty_struct *);
static void	mxser_interrupt(int, void *, struct pt_regs *);
static _INLINE_ void mxser_receive_chars(struct mxser_struct *, int *);
static _INLINE_ void mxser_transmit_chars(struct mxser_struct *);
static _INLINE_ void mxser_check_modem_status(struct mxser_struct *, int);
static int	mxser_block_til_ready(struct tty_struct *, struct file *, struct mxser_struct *);
static int	mxser_startup(struct mxser_struct *);
static void	mxser_shutdown(struct mxser_struct *);
static int	mxser_change_speed(struct mxser_struct *, struct termios *old_termios);
static int	mxser_get_serial_info(struct mxser_struct *, struct serial_struct *);
static int	mxser_set_serial_info(struct mxser_struct *, struct serial_struct *);
static int	mxser_get_lsr_info(struct mxser_struct *, unsigned int *);
static void	mxser_send_break(struct mxser_struct *, int);
static int	mxser_get_modem_info(struct mxser_struct *, unsigned int *);
static int	mxser_set_modem_info(struct mxser_struct *, unsigned int, unsigned int *);

/***************************************************************************/
//fonction permettant de manipuler les registres speciaux

/***************************************************************************/
/* fonction lisant les registres compatible 650				 	    	   */
/* en entree : Base : adresse de base de l'UART							   */
/*			  offset : Offset du registre a lire						   */
/* en sortie : void														   */
/* en retour : la valeur lut dans le registre						 	   */
/***************************************************************************/
unsigned char OxserRead650(unsigned Base,unsigned char Offset)
{
	unsigned char origLCR,Result;

	origLCR = inb(Base + UART_LCR);
	outb(origLCR | LCR_ACCESS_650,Base + UART_LCR);
	Result = inb(Base + Offset);
	outb(origLCR,Base + UART_LCR);
	return Result;
}

/***************************************************************************/
/* fonction ecrivant dans les registres compatible 650					   */
/* en entree : Base : adresse de base de l'UART							   */
/* 			  Offset : Ofsset du registre a ecrire						   */
/* 			  Value  : Valeur a ecrire dans le registre					   */
/* en retour : void														   */
/* en sortie : void 													   */
/***************************************************************************/
void OxserWrite650(unsigned Base, unsigned char Offset, unsigned char Value)
{
	unsigned char origLCR;

	origLCR = inb(Base + UART_LCR);
	outb(origLCR | LCR_ACCESS_650,Base + UART_LCR);
	outb(Value, Base + Offset);
	outb(origLCR,Base + UART_LCR);
}

/***************************************************************************/
/* fonction lisant les registres ICR									   */
/* en entree : info : information sur le port de com					   */
/*			  Offset : offset du registre a lire						   */
/* en sortie : void														   */
/* en retour : la valeur lut dans le registre							   */
/***************************************************************************/
unsigned char OxserReadICR(struct mxser_struct *Info,unsigned char Offset)
{
	unsigned char origACR,Result;
	
	origACR = Info->ACR;

	//validation de la lecture du registre ICR
	outb(UART_ICR_ACR,Info->base + UART_SPR);
	origACR |= ACR_ICR_READ_EN;
	outb(origACR, Info->base + UART_ICR);

	//lecture de la valeur
	outb(Offset,UART_SPR);
	Result = inb(UART_ICR);

	//devalidation de la lecture du registre ICR
	outb(UART_ICR_ACR,Info->base + UART_SPR);
    origACR &= ~ACR_ICR_READ_EN;
    outb(origACR, Info->base + UART_ICR);

	return Result;
}

/***************************************************************************/
/* fonction ecrivant les registres ICR									   */
/* en entree : Info : Les informations sur le com						   */
/*			   Offset : Ofset du registre a ecrire						   */
/*			   Value : Valeur a ecrire dans le registre					   */
/* en sortie : void														   */
/* en retour : void														   */
/***************************************************************************/
void OxserWriteICR(struct mxser_struct *Info,unsigned char Offset, unsigned char Value)
{
	//ecriture dans le registre ICR
	outb(Offset,Info->base + UART_SPR);
	outb(Value, Info->base + UART_ICR);

	//sauvegarde de la valeur du resgistre ACR
	if(Offset == UART_ICR_ACR)
		Info->ACR = Value;
	
}

/***************************************************************************/
/* Fonction validant le registre ASR									   */
/*en entree : Info : les informations sur le port de com				   */
/*en sortie : void														   */
/*en retour : void														   */
/***************************************************************************/
void OxserEnableASR(struct mxser_struct *Info)
{
	unsigned char tempACR;

	tempACR = Info->ACR;
	tempACR |= ACR_ASR_EN;
	OxserWriteICR(Info,UART_ICR_ACR,tempACR);
}

/**************************************************************************/
/*fonction devalidant le registre ASR									  */
/*en entree : info : les informations sur le port de com				  */
/*en sortie : void														  */
/*en retour : void														  */
/**************************************************************************/
void OxserDisableASR(struct mxser_struct *info)
{
	unsigned char tempACR;
	
	tempACR = info->ACR;
	tempACR &= ~ACR_ASR_EN;
	OxserWriteICR(info,UART_ICR_ACR,tempACR);
}
 
/**************************************************************************/
/*fonction lisant le nombre de caractere dans la fifo de reception	      */
/*en entree : info : les informations sur le port de com			   	  */
/*en sortie : void														  */
/*en retour : le nombre de caractere dans la FIFO 						  */
/**************************************************************************/
unsigned char OxserReadRFL(struct mxser_struct * Info)
{
	unsigned char origACR,FillLevel = 0xFF;

	origACR = Info->ACR;
	origACR |= ACR_ASR_EN;
	OxserWriteICR(Info,UART_ICR_ACR,origACR);

	while(FillLevel !=inb(UART_RFL))
	{
		FillLevel = inb(UART_RFL);
	}
	
	origACR &= ACR_ASR_EN;
	OxserWriteICR(Info,UART_ICR_ACR,origACR);
	return FillLevel;
}

/**************************************************************************/
/*fonction lisant le nombre de caractere dans la fifo d'emission          */
/*en entree : info : les informations sur le port de com                  */
/*en sortie : void                                                        */
/*en retour : le nombre de caractere dans la FIFO                         */
/**************************************************************************/
unsigned char OxserReadTFL(struct mxser_struct * Info)
{
	unsigned char origACR,FillLevel = 0xFF;

	origACR = Info->ACR;
	origACR |= ACR_ASR_EN;
	OxserWriteICR(Info,UART_ICR_ACR,origACR);

	while(FillLevel !=inb(UART_TFL))
	{
		FillLevel = inb(UART_TFL);
    }

    origACR &= ACR_ASR_EN;
    OxserWriteICR(Info,UART_ICR_ACR,origACR);
	return FillLevel;
}

/***************************************************************************/
/*
 * The ACKSYS serial driver boot-time initialization code!
 */


#ifdef MODULE
/***************************************************************************/
/*fonction initialisant le module										   */
/*cette fonction est appelee par le systeme lors de l'initialisation du    */
/*du module																   */
/*en entree : void														   */
/*en sortie : void 													       */
/*en retour : la valeur de retour de mxser_init()						   */
/***************************************************************************/
int init_module(void)
{
	int	ret;
	if (verbose)
		printk("Loading module srllinux ...\n");
	ret = mxser_init();
	if (verbose)
		printk("Done.\n");
	return (ret);
}
/***************************************************************************/
/*fonction desactivant le module										   */
/*cette fonction est appelee par le systeme lorsque l'on decharge le module*/
/*elle libere les ressources affectees au module					       */
/*en entree : void														   */
/*en sortie : void													       */
/*en retour : void														   */
/***************************************************************************/
void cleanup_module(void)
{
	int i,err = 0;
	int Port = 0;

	if (verbose)
		printk("Unloading module srllinux ...\n");
/*	if ((err |= tty_unregister_driver(&mxvar_cdriver)))
		printk("Couldn't unregister ACKSYS callout driver\n");*/
	if ((err |= tty_unregister_driver(&mxvar_sdriver)))
		printk("Couldn't unregister ACKSYS serial driver\n");

	for(i=0; i<MXSER_BOARDS; i++)
		{
	    	if(mxsercfg[i].board_type == -1)
	        	continue;
            else
			{
	        	free_irq(mxsercfg[i].irq, &mxvar_table[Port]);
            }
			Port = Port + mxser_numports[mxsercfg[i].board_type-1];
        }

	if (verbose)
		printk("Done.\n");

}
#endif

/**************************************************************************/
/*fonction faisant la configuration de la carte							  */
/*cette fonction enregistre les tty dans le syteme avec leusr parametres  */
/*hardware (adresse de base, irq ....)									  */
/*en entree : NumDevice : Le numero du premier device a creer			  */
/*en sortie : hwconf : la configuration hardware de la carte			  */
/*en retour : 0 : pas d'erreur											  */
/**************************************************************************/
int mxser_initbrd(int *NumDevice,struct mxser_hwconf *hwconf)
{
	struct mxser_struct *	info;
	unsigned long	flags;
        int     retval;
	int	i,n;
	unsigned char tempByte;
	
//	n = board*MXSER_PORTS_PER_BOARD;
	n = *NumDevice;
	info = &mxvar_table[n];
	for ( i=0; i<hwconf->ports; i++, n++, info++ ) {
		if(verbose)
			printk("        %s%d at 0x%04x\n",device_name,n, hwconf->ioaddr[i]);
		if (verbose){
			if ( hwconf->baud_base[i] == 115200 )
		    		printk(" max. baud rate up to 115200 bps.\n");
			else
		    		printk(" max. baud rate up to 921600 bps.\n");
		}
		info->tty = NULL;
		info->port = n;
		info->base = hwconf->ioaddr[i];
		info->irq = hwconf->irq;
		info->vector = hwconf->vector;
		info->vectormask = hwconf->vector_mask;
		info->rx_trigger = 14;
		info->baud_base = hwconf->baud_base[i];
		info->flags = ASYNC_SHARE_IRQ;
		info->type = hwconf->uart_type;
		switch(info->type)
		{
			case PORT_16450 :
			case PORT_8250  :
				info->xmit_fifo_size = 1;
			break;
			
			
			case PORT_OX954 :
				info->xmit_fifo_size = 128;
			break;
			
			default : info->xmit_fifo_size = 16;
		}
		if ( (info->type == PORT_16450) || (info->type == PORT_8250) )
		    info->xmit_fifo_size = 1;
		else
		    info->xmit_fifo_size = 16;
		info->custom_divisor = hwconf->baud_base[i] * 16;
		info->close_delay = 5*HZ/10;
		info->closing_wait = 30*HZ;
		info->tqueue.routine = mxser_do_softint;
		info->tqueue.data = info;
		info->callout_termios = mxvar_cdriver.init_termios;
		info->normal_termios = mxvar_sdriver.init_termios;
		if(info->port == PORT_OX954)
		{
			//validation du mode 950
			tempByte = OxserRead650(info->base,UART_EFR);
			OxserWrite650(info->base,UART_EFR, tempByte | UART_EFR_ECB);	
		}
		/*mise a 1 du DTR*/
		tempByte = inb(info->base + UART_MCR);
		tempByte |= UART_MCR_DTR;
		outb(tempByte,info->base + UART_MCR);
	}

	/*
	 * Allocate the IRQ if necessary
	 */
	save_flags(flags);

//	n = board*MXSER_PORTS_PER_BOARD;
	info = &mxvar_table[n-hwconf->ports];

        cli();
        retval = request_irq(hwconf->irq, mxser_interrupt, IRQ_T(info),
				 "srllinux", info);
	if ( retval ) {
	    restore_flags(flags);
	    //printk("Board %d: %s", board, mxser_brdname[hwconf->board_type-1]);
	    printk("  Request irq fail,IRQ (%d) may be conflit with another device.\n",info->irq);
	    return(retval);
	}
	restore_flags(flags);

	/*recuperation du dernier numero de device*/
	*NumDevice = n;
    return 0;
}


static void mxser_getcfg(int board,struct mxser_hwconf *hwconf)
{
	mxsercfg[board] = *hwconf;
}

/***************************************************************************/
/*fonction verifiant que l'on a bien un UART de type 550             */
/*en entree : l'adresse de base de l'UART                                   */
/*en sorite : void                                                                     */
/*en retour : -1 : L'UART n'est pas de type 550                            */
/*                 1 : l'UART est de type 550                             */
/*************************************************************************/
int CheckUART550(int AddrBase)
{
	unsigned char Byte;

	Byte = inb(AddrBase);
	outb(Byte | UART_LCR_DLAB,AddrBase+ UART_LCR);/*validation du DLAB*/
	/*ecriture d'une valeur dans le resgitre de configuration de l'UART*/
	outb(0x44,AddrBase + UART_DLL);
	outb(0x55,AddrBase + UART_DLM);

	outb(Byte & (~UART_LCR_DLAB), AddrBase + UART_LCR);/*reset du DLAB*/
	/*ici on ne doit plus pouvoir lire DLL et DLM*/
	if((inb(AddrBase + UART_DLL) == 0x44) || (inb(AddrBase + UART_DLM) == 0x55))
	{
		return -1;
	}

	/*verification que l'on peut relir les valeurs*/
	outb(Byte | UART_LCR_DLAB,AddrBase + UART_LCR);/*validation du DLAB*/
	if((inb(AddrBase + UART_DLL) == 0x44) && (inb(AddrBase + UART_DLM) == 0x55))
	{
		outb(Byte & (~UART_LCR_DLAB),AddrBase + UART_LCR);
		return 1;
	}
	else
	{
		outb(Byte & (~UART_LCR_DLAB),AddrBase + UART_LCR);
		return -1;
	}

}

/***************************************************************************/
/*fonction recuperant la configuration PCI de la carte					   */
/*en entree : busnum : le numero de bus de la carte					       */
/* 		      devnum : le numero de device de la carte					   */
/*			  board_type : le type de la carte							   */
/*en sortie : hwconf : la configuration hardware de la carte(info PCI)	   */
/*en retour : 0 : pas d'erreur											   */
/***************************************************************************/
static int mxser_get_PCI_conf(int busnum,int devnum,int board_type,struct mxser_hwconf *hwconf)
{
	int		i;
	unsigned int	val,ioaddress;
	unsigned char Where;
	
	hwconf->board_type = board_type;
	hwconf->ports = mxser_numports[board_type-1];
	/*recuperation de l'@ de base des 4 premiers UART*/
	switch(board_type)
	{
		case MXSER_BOARD_XRSPCI_2: 
		case MXSER_BOARD_XRSPCI_4:
		case MXSER_BOARD_EXT_XRSPCI_4:
			Where = PCI_BASE_ADDRESS_0;
		break;

		case MXSER_BOARD_UNXPCI_4:
		case MXSER_BOARD_UNXPCI_8:
			Where = PCI_BASE_ADDRESS_1;
		break;

		default :
			printk("Unknow board type, driver not initialised\n");
			return MXSER_ERR_IOADDR;
		
	}
	
	pcibios_read_config_dword(busnum,devnum,Where,&val);
	if (val == 0xffffffff)
		return (MXSER_ERR_IOADDR);
	else
		ioaddress = val & 0xffffffc;
	if(verbose)
		printk(KERN_ALERT"base address %x\n",ioaddress);

	for (i = 0; i < hwconf->ports; i++)
		hwconf->ioaddr[i] = ioaddress + 8*i;

	if(board_type == MXSER_BOARD_UNXPCI_4)
	{
		if(CheckUART550(hwconf->ioaddr[i-1] + 8) == 1)
		{
			hwconf->ports = 8;
			hwconf->board_type = MXSER_BOARD_UNXPCI_8;
			for(;i<hwconf->ports;i++)
					hwconf->ioaddr[i] = ioaddress + 8*i;
		}
	}
/*
	pcibios_read_config_dword(busnum,devnum,PCI_BASE_ADDRESS_2,&val);
	if (val == 0xffffffff) 
		return (MXSER_ERR_VECTOR);
	else 
		ioaddress = val & 0xffffffc;
	hwconf->vector =0*/ /*ioaddress+PCI_STATUS_INT*/;
	
	pcibios_read_config_dword(busnum,devnum,PCI_INTERRUPT_LINE,&val);
	if (val == 0xffffffff)
		return (MXSER_ERR_IRQ);
	else 
		hwconf->irq = val & 0xff;
 	if (verbose)
		printk("IRQ level %d\n",hwconf->irq);
					
//	hwconf->uart_type = PORT_16550A;
	hwconf->uart_type =PORT_OX954;
	hwconf->vector_mask = 0;
        for (i = 0; i < hwconf->ports; i++) 
		{
			hwconf->vector_mask |= (1<<i);
			hwconf->baud_base[i] = input_clock / 16;
		}
	return(0);
}

/****************************************************************************/
/*fonction faisant l'initialisation du module								*/
/*cette fonction est appelee par init_module(). Elle a en charge de detecter*/
/*les cartes se trouvant sur le bus PCI et d'enregistrer les devices dans le*/
/*systeme																	*/
/*en entree : void															*/
/*en sortie : void															*/
/*en retour : 0 pas d'erreur												*/
/*			 -1 erreur lors de l'enregistrement des devices 				*/
/****************************************************************************/
int mxser_init(void)
{
	int			i, m, retval, b;
	int			n,index,indexext =0,fin=1;
        int                     ret1, ret2;
	unsigned char		busnum,devnum,tempbusnum,tempdevnum;
	struct mxser_hwconf	hwconf;
	unsigned short typebrd=0;
	int NumDevice = 0;
	int Port=0;
	
	printk("ACKSYS XRS PCI family driver version %s\n",SRLLINUX_VERSION);

	/* Initialize the tty_driver structure */

	memset(&mxvar_sdriver, 0, sizeof(struct tty_driver));
	mxvar_sdriver.magic = TTY_DRIVER_MAGIC;
	mxvar_sdriver.name = device_name;
	mxvar_sdriver.major = ttymajor;
	mxvar_sdriver.minor_start = 0;
	mxvar_sdriver.num = MXSER_PORTS + 1;
	mxvar_sdriver.type = TTY_DRIVER_TYPE_SERIAL;
	mxvar_sdriver.subtype = SERIAL_TYPE_NORMAL;
	mxvar_sdriver.init_termios = tty_std_termios;
	mxvar_sdriver.init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
	mxvar_sdriver.flags = TTY_DRIVER_REAL_RAW;
	mxvar_sdriver.refcount = &mxvar_refcount;
	mxvar_sdriver.table = mxvar_tty;
	mxvar_sdriver.termios = mxvar_termios;
	mxvar_sdriver.termios_locked = mxvar_termios_locked;

	mxvar_sdriver.open = mxser_open;
	mxvar_sdriver.close = mxser_close;
	mxvar_sdriver.write = mxser_write;
	mxvar_sdriver.put_char = mxser_put_char;
	mxvar_sdriver.flush_chars = mxser_flush_chars;
	mxvar_sdriver.write_room = mxser_write_room;
	mxvar_sdriver.chars_in_buffer = mxser_chars_in_buffer;
	mxvar_sdriver.flush_buffer = mxser_flush_buffer;
	mxvar_sdriver.ioctl = mxser_ioctl;
	mxvar_sdriver.throttle = mxser_throttle;
	mxvar_sdriver.unthrottle = mxser_unthrottle;
	mxvar_sdriver.set_termios = mxser_set_termios;
	mxvar_sdriver.stop = mxser_stop;
	mxvar_sdriver.start = mxser_start;
	mxvar_sdriver.hangup = mxser_hangup;

	/*
	 * The callout device is just like normal device except for
	 * major number and the subtype code.
	 */
/*	mxvar_cdriver = mxvar_sdriver;
	mxvar_cdriver.name = cdevice_name;
	mxvar_cdriver.major = calloutmajor;
	mxvar_cdriver.subtype = SERIAL_TYPE_CALLOUT;
*/
	printk("Tty devices major number = %d\n",ttymajor);

	mxvar_diagflag = 0;
	memset(mxvar_table, 0, MXSER_PORTS * sizeof(struct mxser_struct));
	memset(&mxvar_log, 0, sizeof(struct mxser_log));


	m = 0;
/* 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 (mxser_pcibrds)/sizeof (mxser_pciinfo);
		index = 0;
		b = 0;
		while (b < n) 
		{
			if (pcibios_find_device(mxser_pcibrds[b].vendor_id,
						mxser_pcibrds[b].device_id,
						index,
						&busnum,
						&devnum) != 0) {
				b++;
				index = 0;
				continue;
			}
			hwconf.pciInfo.busNum = busnum;
			hwconf.pciInfo.devNum = devnum;
			//printk("Found ACKSYS %s board(BusNo=%d,DevNo=%d)\n",mxser_brdname[mxser_pcibrds[b].board_type-1],busnum,devnum >> 3);
			index++;
			if ( m >= MXSER_BOARDS) {
				printk("Too many ACKSYS boards find (maximum %d),board not configured\n",MXSER_BOARDS);
			}
			else 
			{
				typebrd = mxser_pcibrds[b].board_type;
				do
				{
					fin = 1;
					retval = mxser_get_PCI_conf(busnum,devnum,
					typebrd,&hwconf);
					if (retval < 0) 
					{
						if (retval == MXSER_ERR_IRQ)
							printk("Invalid interrupt number,board not configured\n");
						else if (retval == MXSER_ERR_IRQ_CONFLIT)
							printk("Invalid interrupt number,board not configured\n");
						else if (retval == MXSER_ERR_VECTOR)
							printk("Invalid interrupt vector,board not configured\n");
						else if (retval == MXSER_ERR_IOADDR)
							printk("Invalid I/O address,board not configured\n");
						continue;

					}
	    			if(mxser_initbrd(&NumDevice,&hwconf)<0)
                	     continue;
		    		mxser_getcfg(m,&hwconf);
					m++;
					switch (typebrd)
					{
                   		 case  MXSER_BOARD_XRSPCI_4 :
                         	if(pcibios_find_device(mxser_pcibrds[b].vendor_id,
                                    PCI_DEVICE_ID_EXT_XRSPCI_4,
                                    indexext,
                                    &tempbusnum,
                                    &tempdevnum)==0)
                            {
                           	 	/*on a trouve une extension*/
                            	if(tempbusnum==busnum && (tempdevnum>>3)==(devnum>>3))
                            	{
                            		typebrd = MXSER_BOARD_EXT_XRSPCI_4;
	                                indexext++;
    	                            busnum = hwconf.pciInfo.busNum = tempbusnum;
        	                        devnum = hwconf.pciInfo.devNum = tempdevnum;
            	                    fin = 0;
                	             }
                             }
                         break;
					}
				}while(fin!=1);
			}
			printk("found ACKSYS %s board (BusNo=%d, DevNo=%d)\n",mxser_brdname[hwconf.board_type-1],
							busnum,devnum>>3);
		}
	}
#endif
	for(i=m; i<MXSER_BOARDS; i++){
		mxsercfg[i].board_type = -1;
	}


        ret1 = 0;
        ret2 = 0;
        if ( !(ret1=tty_register_driver(&mxvar_sdriver)) )
		{
/*            if ( !(ret2=tty_register_driver(&mxvar_cdriver)) )
			{
				return 0;
            }
			else
			{
                tty_unregister_driver(&mxvar_sdriver);
				printk("Couldn't install ACKSYS callout driver !\n");
            }*/
        }
		else
	    	printk("Couldn't install ACKSYS driver !\n");


        if(ret1/* || ret2*/){
            for(i=0; i<MXSER_BOARDS; i++)
			{
	        	if(mxsercfg[i].board_type == -1)
	           		 continue;
                else
				{
				    free_irq(mxsercfg[i].irq, &mxvar_table[Port]);
                    Port = Port + mxser_numports[mxsercfg[i].board_type-1];

                }
            }
            return -1;
        }
	return(0);
}

static void mxser_do_softint(void *private_)
{
	struct mxser_struct *	info = (struct mxser_struct *)private_;
	struct tty_struct *	tty;

	tty = info->tty;
	if ( !tty )
	    return;
#ifdef  __SMP__
	lock_kernel();
#endif
#if (LINUX_VERSION_CODE <  VERSION_CODE(2,1,0))
	if ( clear_bit(MXSER_EVENT_TXLOW, &info->event) ) {
	    if ( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
		 tty->ldisc.write_wakeup )
		(tty->ldisc.write_wakeup)(tty);
	    wake_up_interruptible(&tty->write_wait);
	}
	if ( clear_bit(MXSER_EVENT_HANGUP, &info->event) ) {
		tty_hangup(tty);
	}
#else
	if ( test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event) ) {
	    if ( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
		 tty->ldisc.write_wakeup )
		(tty->ldisc.write_wakeup)(tty);
	    wake_up_interruptible(&tty->write_wait);
	}
	if ( test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event) ) {
		tty_hangup(tty);
	}
#endif
#ifdef  __SMP__
	unlock_kernel();
#endif
}

/*
 * This routine is called whenever a serial port is opened.  It
 * enables interrupts for a serial port, linking in its async structure into
 * the IRQ chain.   It also performs the serial-specific
 * initialization for the tty structure.
 */
static int mxser_open(struct tty_struct * tty, struct file * filp)
{
	struct mxser_struct *	info;
	int			retval, line;
	unsigned long		page;
	
	line = PORTNO(tty);
	if ( line == MXSER_PORTS )
	    return(0);
	if ( (line < 0) || (line > MXSER_PORTS) )
	    return(-ENODEV);
	info = mxvar_table + line;
	if ( !info->base )
	    return(-ENODEV);

	info->count++;
	tty->driver_data = info;
	info->tty = tty;
	if ( !mxvar_tmp_buf ) {
	    page = get_free_page(GFP_KERNEL);
	    if ( !page )
		return(-ENOMEM);
	    if ( mxvar_tmp_buf )
		free_page(page);
	    else
		mxvar_tmp_buf = (unsigned char *)page;
	}
	
	/*reset de la voie*/
	if(info->type == PORT_OX954)
		OxserWriteICR(info,UART_ICR_CSR,0x00);
	/*
	 * Start up serial port
	 */
	retval = mxser_startup(info);
	if ( retval )
	    return(retval);

	retval = mxser_block_til_ready(tty, filp, info);
	if ( retval )
	    return(retval);

	if ( (info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS) ) {
	    if ( tty->driver.subtype == SERIAL_TYPE_NORMAL )
		*tty->termios = info->normal_termios;
	    else
		*tty->termios = info->callout_termios;
	    mxser_change_speed(info, 0);
	}

	info->session = current->session;
	info->pgrp = current->pgrp;

	MOD_INC_USE_COUNT;

	return(0);
}

/*
 * This routine is called when the serial port gets closed.  First, we
 * wait for the last remaining data to be sent.  Then, we unlink its
 * async structure from the interrupt chain if necessary, and we free
 * that IRQ if nothing is left in the chain.
 */
static void mxser_close(struct tty_struct * tty, struct file * filp)
{
	struct mxser_struct * info = (struct mxser_struct *)tty->driver_data;
	unsigned long	flags;
	unsigned long	timeout;

	if ( PORTNO(tty) == MXSER_PORTS )
	    return;
	if ( !info )
	    return;

	save_flags(flags);
	cli();

	if ( tty_hung_up_p(filp) ) {
	    restore_flags(flags);
	    MOD_DEC_USE_COUNT;
	    return;
	}

	if ( (tty->count == 1) && (info->count != 1) ) {
	    /*
	     * Uh, oh.	tty->count is 1, which means that the tty
	     * structure will be freed.  Info->count should always
	     * be one in these conditions.  If it's greater than
	     * one, we've got real problems, since it means the
	     * serial port won't be shutdown.
	     */
	    printk("mxser_close: bad serial port count; tty->count is 1, "
		   "info->count is %d\n", info->count);
	    info->count = 1;
	}
	if ( --info->count < 0 ) {
	    printk("mxser_close: bad serial port count for ttys%d: %d\n",
		   info->port, info->count);
	    info->count = 0;
	}
	if ( info->count ) {
	    restore_flags(flags);
	    MOD_DEC_USE_COUNT;
	    return;
	}
	info->flags |= ASYNC_CLOSING;
	/*
	 * Save the termios structure, since this port may have
	 * separate termios for callout and dialin.
	 */
	if ( info->flags & ASYNC_NORMAL_ACTIVE )
	    info->normal_termios = *tty->termios;
	if ( info->flags & ASYNC_CALLOUT_ACTIVE )
	    info->callout_termios = *tty->termios;
	/*
	 * Now we wait for the transmit buffer to clear; and we notify
	 * the line discipline to only process XON/XOFF characters.
	 */
	tty->closing = 1;
	if ( info->closing_wait != ASYNC_CLOSING_WAIT_NONE )
	    tty_wait_until_sent(tty, info->closing_wait);
	/*
	 * At this point we stop accepting input.  To do this, we
	 * disable the receive line status interrupts, and tell the
	 * interrupt driver to stop checking the data ready bit in the
	 * line status register.
	 */
	info->IER &= ~UART_IER_RLSI;
/* by William
	info->read_status_mask &= ~UART_LSR_DR;
*/
	if ( info->flags & ASYNC_INITIALIZED ) {
	    outb(info->IER, info->base + UART_IER);
	    /*
	     * Before we drop DTR, make sure the UART transmitter
	     * has completely drained; this is especially
	     * important if there is a transmit FIFO!
	     */
	    timeout = jiffies + HZ;
	    while ( !(inb(info->base + UART_LSR) & UART_LSR_TEMT) ) {
		current->state = TASK_INTERRUPTIBLE;
		schedule_timeout(5);
		if ( jiffies > timeout )
		    break;
	    }
	}
	mxser_shutdown(info);
	if ( tty->driver.flush_buffer )
	    tty->driver.flush_buffer(tty);
	if ( tty->ldisc.flush_buffer )
	    tty->ldisc.flush_buffer(tty);
	tty->closing = 0;
	info->event = 0;
	info->tty = 0;
	if ( info->blocked_open ) {
	    if ( info->close_delay ) {
		current->state = TASK_INTERRUPTIBLE;
		schedule_timeout(info->close_delay);
	    }
	    wake_up_interruptible(&info->open_wait);
	}
	info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE |
			 ASYNC_CLOSING);
	wake_up_interruptible(&info->close_wait);
	restore_flags(flags);

	MOD_DEC_USE_COUNT;
}

static int mxser_write(struct tty_struct * tty, int from_user,
		       const unsigned char * buf, int count)
{
	int		c, total = 0;
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;
	unsigned long	flags;
	
	if ( !tty || !info->xmit_buf || !mxvar_tmp_buf )
	    return(0);

	if ( from_user )
	    down(&mxvar_tmp_buf_sem);
	save_flags(flags);
	while ( 1 ) {
	    cli();
	    c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
			       SERIAL_XMIT_SIZE - info->xmit_head));
	    if ( c <= 0 )
		break;

	    if ( from_user ) {
		copy_from_user(mxvar_tmp_buf, buf, c);
		c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
			       SERIAL_XMIT_SIZE - info->xmit_head));
		memcpy(info->xmit_buf + info->xmit_head, mxvar_tmp_buf, c);
	    } else
		memcpy(info->xmit_buf + info->xmit_head, buf, c);
	    info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1);
	    info->xmit_cnt += c;
	    restore_flags(flags);
	    buf += c;
	    count -= c;
	    total += c;
	}
	if ( from_user )
	    up(&mxvar_tmp_buf_sem);
	if ( info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
		!(info->IER & UART_IER_THRI) ) {
	    info->IER |= UART_IER_THRI;
		outb(info->IER, info->base + UART_IER);
	}
	restore_flags(flags);
	return(total);
}

static void mxser_put_char(struct tty_struct * tty, unsigned char ch)
{
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;
	unsigned long	flags;

	if ( !tty || !info->xmit_buf )
	    return;

	save_flags(flags);
	cli();
	if ( info->xmit_cnt >= SERIAL_XMIT_SIZE - 1 ) {
	    restore_flags(flags);
	    return;
	}

	info->xmit_buf[info->xmit_head++] = ch;
	info->xmit_head &= SERIAL_XMIT_SIZE - 1;
	info->xmit_cnt++;
	/********************************************** why ??? ***********
	if ( !tty->stopped && !tty->hw_stopped &&
	     !(info->IER & UART_IER_THRI) ) {
	    info->IER |= UART_IER_THRI;
	    outb(info->IER, info->base + UART_IER);
	}
	*****************************************************************/
	restore_flags(flags);
}

static void mxser_flush_chars(struct tty_struct * tty)
{
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;
	unsigned long	flags;

	if ( info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
	     !info->xmit_buf )
	    return;

	save_flags(flags);
	cli();
	info->IER |= UART_IER_THRI;
	outb(info->IER, info->base + UART_IER);
	restore_flags(flags);
}

static int mxser_write_room(struct tty_struct * tty)
{
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;
	int	ret;

	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
	if ( ret < 0 )
	    ret = 0;
	return(ret);
}

static int mxser_chars_in_buffer(struct tty_struct * tty)
{
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;

	return(info->xmit_cnt);
}

static void mxser_flush_buffer(struct tty_struct * tty)
{
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;

	cli();
	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
	sti();
	wake_up_interruptible(&tty->write_wait);
	if ( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
	     tty->ldisc.write_wakeup )
	    (tty->ldisc.write_wakeup)(tty);
}

static int mxser_ioctl(struct tty_struct * tty, struct file * file,
		       unsigned int cmd, unsigned long arg)
{
	int			error;
	struct mxser_struct *	info = (struct mxser_struct *)tty->driver_data;
	int			retval;
	struct async_icount	cprev, cnow;	    /* kernel counter temps */
	struct serial_icounter_struct *p_cuser;     /* user space */
	unsigned long 		templ;
	unsigned char       tempb;
	
	if ( PORTNO(tty) == MXSER_PORTS )
	    return(mxser_ioctl_special(cmd, arg));
	if ( (cmd != TIOCGSERIAL) && (cmd != TIOCMIWAIT) &&
	     (cmd != TIOCGICOUNT) ) {
	    if ( tty->flags & (1 << TTY_IO_ERROR) )
		return(-EIO);
	}
	switch ( cmd ) {
	case TCSBRK:	/* SVID version: non-zero arg --> no break */
	    retval = tty_check_change(tty);
	    if ( retval )
		return(retval);
	    tty_wait_until_sent(tty, 0);
	    if ( !arg )
		mxser_send_break(info, HZ/4);		/* 1/4 second */
	    return(0);
	case TCSBRKP:	/* support for POSIX tcsendbreak() */
	    retval = tty_check_change(tty);
	    if ( retval )
		return(retval);
	    tty_wait_until_sent(tty, 0);
	    mxser_send_break(info, arg ? arg*(HZ/10) : HZ/4);
	    return(0);
	case TIOCGSOFTCAR:
	    error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(long));
	    if ( error )
		return(error);
	    put_to_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg);
	    return(0);
	case TIOCSSOFTCAR:
	    error = verify_area(VERIFY_READ, (void *)arg, sizeof(long));
	    if ( error )
		return(error);
	    get_from_user(templ,(unsigned long *)arg);
	    arg = templ;
	    tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) |
				    (arg ? CLOCAL : 0));
	    return(0);
	case TIOCMGET:
	    error = verify_area(VERIFY_WRITE, (void *)arg,
				sizeof(unsigned int));
	    if ( error )
		return(error);
	    return(mxser_get_modem_info(info, (unsigned int *)arg));
	case TIOCMBIS:
	case TIOCMBIC:
	case TIOCMSET:
	    return(mxser_set_modem_info(info, cmd, (unsigned int *)arg));
	case TIOCGSERIAL:
	    error = verify_area(VERIFY_WRITE, (void *)arg,
				sizeof(struct serial_struct));
	    if ( error )
		return(error);
	    return(mxser_get_serial_info(info, (struct serial_struct *)arg));
	case TIOCSSERIAL:
	    error = verify_area(VERIFY_READ, (void *)arg,
				sizeof(struct serial_struct));
	    if ( error )
		return(error);
	    return(mxser_set_serial_info(info, (struct serial_struct *)arg));
	case TIOCSERGETLSR: /* Get line status register */
	    error = verify_area(VERIFY_WRITE, (void *)arg,
				sizeof(unsigned int));
	    if ( error )
		return(error);
	    else
		return(mxser_get_lsr_info(info, (unsigned int *)arg));
	/*
	 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
	 * - mask passed in arg for lines of interest
	 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
	 * Caller should use TIOCGICOUNT to see which one it was
	 */
	case TIOCMIWAIT:
	    cli();
	    cprev = info->icount;   /* note the counters on entry */
	    sti();
	    while ( 1 ) {
		interruptible_sleep_on(&info->delta_msr_wait);
		/* see if a signal did it */
		if ( signal_pending(current) )
		    return(-ERESTARTSYS);
		cli();
		cnow = info->icount;	/* atomic copy */
		sti();
		if ( cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
		     cnow.dcd == cprev.dcd && cnow.cts == cprev.cts )
		    return(-EIO);	/* no change => error */
		if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
		     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
		     ((arg & TIOCM_CD)	&& (cnow.dcd != cprev.dcd)) ||
		     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
		    return(0);
		}
		cprev = cnow;
	    }
	    /* NOTREACHED */
	/*
	 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
	 * Return: write counters to the user passed counter struct
	 * NB: both 1->0 and 0->1 transitions are counted except for
	 *     RI where only 0->1 is counted.
	 */
	case TIOCGICOUNT:
	    error = verify_area(VERIFY_WRITE, (void *)arg,
				sizeof(struct serial_icounter_struct));
	    if ( error )
		return(error);
	    cli();
	    cnow = info->icount;
	    sti();
	    p_cuser = (struct serial_icounter_struct *)arg;
	    put_to_user(cnow.cts, &p_cuser->cts);
	    put_to_user(cnow.dsr, &p_cuser->dsr);
	    put_to_user(cnow.rng, &p_cuser->rng);
	    put_to_user(cnow.dcd, &p_cuser->dcd);
	    return(0);
	case ACKSYS_HighSpeedOn:
	    error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
	    if ( error )
		return(error);
	    put_to_user(info->baud_base != 115200 ? 1 : 0, (int *)arg);
	    return(0);
	case ACKSYS_RESET :
		OxserWriteICR(info,UART_ICR_CSR,0x00);
		return 0;
	case ACKSYS_READ_REGISTRE :
		tempb = inb(info->base+2);
		put_to_user(tempb,(unsigned char*)arg);	
		return (0);
	case ACKSYS_ENABLE_485_MODE :
		tempb = info->ACR | (ACR_DTR_RS485_EN | ACR_DTR_RS485_SENSE);
	    OxserWriteICR(info,UART_ICR_ACR,tempb);	
		return 0;
	case ACKSYS_DISABLE_485_MODE :
	    tempb = info->ACR & (~ACR_DTR_RS485_EN | ~ACR_DTR_RS485_SENSE);
	    OxserWriteICR(info,UART_ICR_ACR,tempb);
		return 0;
	case ACKSYS_SET_TRIGGER :
		error = verify_area(VERIFY_READ, (void *)arg, sizeof(TTrigger));
		if (error)
		{
			return error;
		}
		OxserWriteICR(info,UART_ICR_TTL,((TTrigger *)arg)->TxTrigger);	
		if((((TTrigger *)arg)->RxTrigger)>=1)
			OxserWriteICR(info,UART_ICR_RTL,((TTrigger *)arg)->RxTrigger);

		/*validation des triggers*/
		tempb = info->ACR |ACR_950TRIG_LEV_EN;
		OxserWriteICR(info,UART_ICR_ACR,tempb);
		return 0;
	case ACKSYS_DISABLE_TRIGGER :
		tempb =info->ACR & ~ACR_950TRIG_LEV_EN;
		OxserWriteICR(info,UART_ICR_ACR,tempb);
		return 0;
	default:
		return(-ENOIOCTLCMD);
	}
	return(0);
}

static int mxser_ioctl_special(unsigned int cmd, unsigned long arg)
{
	int		error, i, result, status;

	switch ( cmd ) {
	case ACKSYS_GET_CONF:
	    error = verify_area(VERIFY_WRITE, (void *)arg, 
			    sizeof(struct mxser_hwconf)*4);
	    if ( error )
		return(error);
	    copy_to_user((struct mxser_hwconf *)arg, mxsercfg, 
			    sizeof(struct mxser_hwconf)*4);
	    return 0;
        case ACKSYS_GET_MAJOR:
	    error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
	    if ( error )
                return(error);
	    copy_to_user((int*)arg, &ttymajor, sizeof(int));
            return 0;
/*
        case ACKSYS_GET_CUMAJOR:
	    error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
	    if ( error )
                return(error);
	    copy_to_user((int*)arg, &calloutmajor, sizeof(int));
            return 0;
*/
	case ACKSYS_CHKPORTENABLE:
	    error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(long));
	    if ( error )
		return(error);
	    result = 0;
	    for ( i=0; i<MXSER_PORTS; i++ ) {
		if ( mxvar_table[i].base )
		    result |= (1 << i);
	    }
	    put_to_user(result, (unsigned long *)arg);
	    return(0);
	case ACKSYS_GETDATACOUNT:
	    error = verify_area(VERIFY_WRITE, (void *)arg,
				sizeof(struct mxser_log));
	    if ( error )
		return(error);
	    copy_to_user((struct mxser_log *)arg, &mxvar_log, sizeof(mxvar_log));
	    return(0);
        case ACKSYS_GETMSTATUS:
	    error = verify_area(VERIFY_WRITE, (void *)arg,
				sizeof(struct mxser_mstatus) * MXSER_PORTS);
	    if ( error )
		return(error);

            for(i=0; i<MXSER_PORTS; i++){
                GMStatus[i].ri = 0;
		if ( !mxvar_table[i].base ){
                    GMStatus[i].dcd = 0;
                    GMStatus[i].dsr = 0;
                    GMStatus[i].cts = 0;
		    continue;
                }

	        if ( !mxvar_table[i].tty || !mxvar_table[i].tty->termios )
                    GMStatus[i].cflag=mxvar_table[i].normal_termios.c_cflag;
                else
                    GMStatus[i].cflag = mxvar_table[i].tty->termios->c_cflag;
                
                status = inb(mxvar_table[i].base + UART_MSR);
                if(status  & 0x80/*UART_MSR_DCD*/)
                    GMStatus[i].dcd = 1;
                else
                    GMStatus[i].dcd = 0;

                if(status  & 0x20/*UART_MSR_DSR*/)
                    GMStatus[i].dsr = 1;
                else
                    GMStatus[i].dsr = 0;
                

                if(status  & 0x10/*UART_MSR_CTS*/)
                    GMStatus[i].cts = 1;
                else
                    GMStatus[i].cts = 0;
            }
            copy_to_user((struct mxser_mstatus *)arg, GMStatus,
                                     sizeof(struct mxser_mstatus) * MXSER_PORTS);
            return 0;
	default:
	    return(-ENOIOCTLCMD);
	}
	return(0);
}

/*
 * This routine is called by the upper-layer tty layer to signal that
 * incoming characters should be throttled.
 */
static void mxser_throttle(struct tty_struct * tty)
{
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;
	unsigned long	flags;

	if ( I_IXOFF(tty) ) {
	    info->x_char = STOP_CHAR(tty);
	    save_flags(flags);
	    cli();
	    outb(info->IER, 0);
	    info->IER |= UART_IER_THRI;
	    outb(info->IER, info->base + UART_IER);	/* force Tx interrupt */
	    restore_flags(flags);
	}

	if ( info->tty->termios->c_cflag & CRTSCTS ) {
	    info->MCR &= ~UART_MCR_RTS;
	    save_flags(flags);
	    cli();
	    outb(info->MCR, info->base + UART_MCR);
	    restore_flags(flags);
	}
}

static void mxser_unthrottle(struct tty_struct * tty)
{
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;
	unsigned long	flags;

	if ( I_IXOFF(tty) ) {
	    if ( info->x_char )
		info->x_char = 0;
	    else {
		info->x_char = START_CHAR(tty);
		save_flags(flags);
		cli();
		outb(info->IER, 0);
		info->IER |= UART_IER_THRI;		/* force Tx interrupt */
		outb(info->IER, info->base + UART_IER);
		restore_flags(flags);
	    }
	}

	if ( info->tty->termios->c_cflag & CRTSCTS ) {
	    info->MCR |= UART_MCR_RTS;
	    save_flags(flags);
	    cli();
	    outb(info->MCR, info->base + UART_MCR);
	    restore_flags(flags);
	}
}

static void mxser_set_termios(struct tty_struct * tty,
			      struct termios * old_termios)
{
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;

/* 8-2-99 by William
	if ( (tty->termios->c_cflag == old_termios->c_cflag) &&
	     (RELEVANT_IFLAG(tty->termios->c_iflag) ==
	      RELEVANT_IFLAG(old_termios->c_iflag)) )
	    return;

	mxser_change_speed(info, old_termios);

	if ( (old_termios->c_cflag & CRTSCTS) &&
	     !(tty->termios->c_cflag & CRTSCTS) ) {
	    tty->hw_stopped = 0;
	    mxser_start(tty);
	}
*/
	if ( (tty->termios->c_cflag != old_termios->c_cflag) ||
	     (RELEVANT_IFLAG(tty->termios->c_iflag) !=
	      RELEVANT_IFLAG(old_termios->c_iflag)) ) {

		mxser_change_speed(info, old_termios);

		if ( (old_termios->c_cflag & CRTSCTS) &&
	     	     !(tty->termios->c_cflag & CRTSCTS) ) {
	    		tty->hw_stopped = 0;
	    		mxser_start(tty);
		}
	}
	
/* Handle sw stopped */
	if ( (old_termios->c_iflag & IXON) &&
     	     !(tty->termios->c_iflag & IXON) ) {
    		tty->stopped = 0;
    		mxser_start(tty);
	}


}

/*
 * mxser_stop() and mxser_start()
 *
 * This routines are called before setting or resetting tty->stopped.
 * They enable or disable transmitter interrupts, as necessary.
 */
static void mxser_stop(struct tty_struct * tty)
{
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;
	unsigned long	flags;

	save_flags(flags);
	cli();
	if ( info->IER & UART_IER_THRI ) {
	    info->IER &= ~UART_IER_THRI;
	    outb(info->IER, info->base + UART_IER);
	}
	restore_flags(flags);
}

static void mxser_start(struct tty_struct * tty)
{
	struct mxser_struct *info = (struct mxser_struct *)tty->driver_data;
	unsigned long	flags;

	save_flags(flags);
	cli();
	if ( info->xmit_cnt && info->xmit_buf &&
	     !(info->IER & UART_IER_THRI) ) {
	    info->IER |= UART_IER_THRI;
	    outb(info->IER, info->base + UART_IER);
	}
	restore_flags(flags);
}

/*
 * This routine is called by tty_hangup() when a hangup is signaled.
 */
void mxser_hangup(struct tty_struct * tty)
{
	struct mxser_struct * info = (struct mxser_struct *)tty->driver_data;

	mxser_flush_buffer(tty);
	mxser_shutdown(info);
	info->event = 0;
	info->count = 0;
	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
	info->tty = 0;
	wake_up_interruptible(&info->open_wait);
}

/*
 * This is the serial driver's generic interrupt routine
 */
static void mxser_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
	int			status, i;
	struct mxser_struct *	info;
	struct mxser_struct *	port;
	int			max,msr;
	int			pass_counter = 0;
	int nbport = 0;
	
		port=0;
        //recherche de la carte qui a generer l'it
	   	for(i=0; i<MXSER_BOARDS; i++)
		{
			if(dev_id == &(mxvar_table[nbport]))
			{
                port = dev_id;
                break;
            }
			//on passe a la carte suivante
			nbport = nbport + mxser_numports[mxsercfg[i].board_type-1];
        }
		if(i==MXSER_BOARDS)
            return;
        if(port==0)
            return;
        max = mxser_numports[mxsercfg[i].board_type-1];
	//recherche du port qui a generer l'it
	while ( 1 ) 
	{
	    for ( i=0 ; i<max; i++) 
		{
			info = port + i;
			if ( !info->tty ||(inb(info->base + UART_IIR) & UART_IIR_NO_INT) )
			{
				continue;
			}
			//traitement de l'it
			status = inb(info->base + UART_LSR) & info->read_status_mask;
//			status &= info->read_status_mask;
			if ( status & UART_LSR_DR )
		    	mxser_receive_chars(info, &status);
			msr = inb(info->base + UART_MSR);
			if ( msr & UART_MSR_ANY_DELTA )
		    	mxser_check_modem_status(info, msr);
			if ( status & UART_LSR_THRE ) 
			{
/* 8-2-99 by William
		    	if ( info->x_char || (info->xmit_cnt > 0) )
*/
				mxser_transmit_chars(info);
			}
	    }
	    if ( pass_counter++ > MXSER_ISR_PASS_LIMIT ) {
			break;/*	Prevent infinite loops */
		
	    }
	}
}

static _INLINE_ void mxser_receive_chars(struct mxser_struct *info,
					 int *status)
{
	struct tty_struct *	tty = info->tty;
	unsigned char		ch;
	int			ignored = 0;
	int			cnt = 0;

	do {
	    ch = inb(info->base + UART_RX);
	    if ( *status & info->ignore_status_mask ) {
		if ( ++ignored > 100 )
		    break;
	    } else {
		if ( tty->flip.count >= TTY_FLIPBUF_SIZE )
		    break;
		tty->flip.count++;
		if ( *status & UART_LSR_SPECIAL ) {
		    if ( *status & UART_LSR_BI ) {
			*tty->flip.flag_buf_ptr++ = TTY_BREAK;
			if ( info->flags & ASYNC_SAK )
			    do_SAK(tty);
		    } else if ( *status & UART_LSR_PE ) {
			*tty->flip.flag_buf_ptr++ = TTY_PARITY;
		    } else if ( *status & UART_LSR_FE ) {
			*tty->flip.flag_buf_ptr++ = TTY_FRAME;
		    } else if ( *status & UART_LSR_OE ) {
			*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
		    } else
			*tty->flip.flag_buf_ptr++ = 0;
		} else
		    *tty->flip.flag_buf_ptr++ = 0;
		*tty->flip.char_buf_ptr++ = ch;
		cnt++;
	    }
	    *status = inb(info->base + UART_LSR) & info->read_status_mask;
	} while ( *status & UART_LSR_DR );
	mxvar_log.rxcnt[info->port] += cnt;
#if (LINUX_VERSION_CODE >= VERSION_CODE(2,1,0))
	queue_task(&tty->flip.tqueue, &tq_timer);
#else
	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
#endif

}

static _INLINE_ void mxser_transmit_chars(struct mxser_struct *info)
{
	int	count, cnt;
	
	if ( info->x_char ) {
	    outb(info->x_char, info->base + UART_TX);
	    info->x_char = 0;
	    mxvar_log.txcnt[info->port]++;
	    return;
	}
	if ( (info->xmit_cnt <= 0) || info->tty->stopped ||
	     info->tty->hw_stopped ) {
	    info->IER &= ~UART_IER_THRI;
	    outb(info->IER, info->base + UART_IER);
	    return;
	}

	cnt = info->xmit_cnt;
	count = info->xmit_fifo_size;
	do {
	    outb(info->xmit_buf[info->xmit_tail++], info->base + UART_TX);
	    info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1);
	    if ( --info->xmit_cnt <= 0 )
		break;
	} while ( --count > 0 );
	mxvar_log.txcnt[info->port] += (cnt - info->xmit_cnt);

	if ( info->xmit_cnt < WAKEUP_CHARS ) {
		set_bit(MXSER_EVENT_TXLOW,&info->event);
		queue_task(&info->tqueue,&tq_scheduler);
	}

	if ( info->xmit_cnt <= 0 ) {
	    info->IER &= ~UART_IER_THRI;
	    outb(info->IER, info->base + UART_IER);
	}
}

static _INLINE_ void mxser_check_modem_status(struct mxser_struct *info,
					      int status)
{

	/* update input line counters */
	if ( status & UART_MSR_TERI )
	    info->icount.rng++;
	if ( status & UART_MSR_DDSR )
	    info->icount.dsr++;
	if ( status & UART_MSR_DDCD )
	    info->icount.dcd++;
	if ( status & UART_MSR_DCTS )
	    info->icount.cts++;
	wake_up_interruptible(&info->delta_msr_wait);

	if ( (info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD) ) {
	    if ( status & UART_MSR_DCD )
		wake_up_interruptible(&info->open_wait);
	    else if ( !((info->flags & ASYNC_CALLOUT_ACTIVE) &&
		      (info->flags & ASYNC_CALLOUT_NOHUP)) )

	       set_bit(MXSER_EVENT_HANGUP,&info->event);
#if (LINUX_VERSION_CODE >= VERSION_CODE(2,1,0))
		queue_task(&info->tqueue, &tq_scheduler);
#else
		queue_task_irq_off(&info->tqueue, &tq_scheduler);
#endif

	}
	if ( info->flags & ASYNC_CTS_FLOW ) {
	    if ( info->tty->hw_stopped ) {
		if ( status & UART_MSR_CTS ) {
		    info->tty->hw_stopped = 0;
		    info->IER |= UART_IER_THRI;
		    outb(info->IER, info->base + UART_IER);

	            set_bit(MXSER_EVENT_TXLOW,&info->event);
#if (LINUX_VERSION_CODE >= VERSION_CODE(2,1,0))
		    queue_task(&info->tqueue, &tq_scheduler);
#else
		    queue_task_irq_off(&info->tqueue, &tq_scheduler);
#endif
		}
	    } else {
		if ( !(status & UART_MSR_CTS) ) {
		    info->tty->hw_stopped = 1;
		    info->IER &= ~UART_IER_THRI;
		    outb(info->IER, info->base + UART_IER);
		}
	    }
	}
}

static int mxser_block_til_ready(struct tty_struct *tty, struct file * filp,
				 struct mxser_struct *info)
{
	struct wait_queue	wait = { current, NULL };
	int			retval;
	int			do_clocal = 0;

	/*
	 * 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) || (info->flags & ASYNC_CLOSING) ) {
	    if ( info->flags & ASYNC_CLOSING )
		interruptible_sleep_on(&info->close_wait);
#ifdef SERIAL_DO_RESTART
	    if ( info->flags & 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 ( info->flags & ASYNC_NORMAL_ACTIVE )
		return(-EBUSY);
	    if ( (info->flags & ASYNC_CALLOUT_ACTIVE) &&
		 (info->flags & ASYNC_SESSION_LOCKOUT) &&
		 (info->session != current->session) )
		return(-EBUSY);
	    if ( (info->flags & ASYNC_CALLOUT_ACTIVE) &&
		 (info->flags & ASYNC_PGRP_LOCKOUT) &&
		 (info->pgrp != current->pgrp) )
		return(-EBUSY);
	    info->flags |= ASYNC_CALLOUT_ACTIVE;
	    return(0);
	}

	/*
	 * If non-blocking mode is set, or the port is not enabled,
	 * then make the check up front and then exit.
	 */
	if ( (filp->f_flags & O_NONBLOCK) ||
	     (tty->flags & (1 << TTY_IO_ERROR)) ) {
	    if ( info->flags & ASYNC_CALLOUT_ACTIVE )
		return(-EBUSY);
	    info->flags |= ASYNC_NORMAL_ACTIVE;
	    return(0);
	}

	if ( info->flags & ASYNC_CALLOUT_ACTIVE ) {
	    if ( info->normal_termios.c_cflag & CLOCAL )
		do_clocal = 1;
	} else {
	    if ( tty->termios->c_cflag & CLOCAL )
		do_clocal = 1;
	}

	/*
	 * Block waiting for the carrier detect and the line to become
	 * free (i.e., not in use by the callout).  While we are in
	 * this loop, info->count is dropped by one, so that
	 * mxser_close() knows when to free things.  We restore it upon
	 * exit, either normal or abnormal.
	 */
	retval = 0;
	add_wait_queue(&info->open_wait, &wait);
	cli();
	if ( !tty_hung_up_p(filp) )
	    info->count--;
	sti();
	info->blocked_open++;
	while ( 1 ) {
	    cli();
	    if ( !(info->flags & ASYNC_CALLOUT_ACTIVE) )
		outb(inb(info->base + UART_MCR) | UART_MCR_DTR | UART_MCR_RTS,
		     info->base + UART_MCR);
	    sti();
	    current->state = TASK_INTERRUPTIBLE;
	    if ( tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED) ) {
#ifdef SERIAL_DO_RESTART
		if ( info->flags & ASYNC_HUP_NOTIFY )
		    retval = -EAGAIN;
		else
		    retval = -ERESTARTSYS;
#else
		retval = -EAGAIN;
#endif
		break;
	    }
	    if ( !(info->flags & ASYNC_CALLOUT_ACTIVE) &&
		 !(info->flags & ASYNC_CLOSING) &&
		 (do_clocal || (inb(info->base + UART_MSR) & UART_MSR_DCD)) )
		break;
	    if ( signal_pending(current) ) {
		retval = -ERESTARTSYS;
		break;
	    }
	    schedule();
	}
	current->state = TASK_RUNNING;
	remove_wait_queue(&info->open_wait, &wait);
	if ( !tty_hung_up_p(filp) )
	    info->count++;
	info->blocked_open--;
	if ( retval )
	    return(retval);
	info->flags |= ASYNC_NORMAL_ACTIVE;
	return(0);
}

static int mxser_startup(struct mxser_struct * info)
{
	unsigned long	flags;
	unsigned long	page;

	page = get_free_page(GFP_KERNEL);
	if ( !page )
	    return(-ENOMEM);

	save_flags(flags);
	cli();

	if ( info->flags & ASYNC_INITIALIZED ) {
	    free_page(page);
	    restore_flags(flags);
	    return(0);
	}

	if ( !info->base || !info->type ) {
	    if ( info->tty )
		set_bit(TTY_IO_ERROR, &info->tty->flags);
	    free_page(page);
	    restore_flags(flags);
	    return(0);
	}
	if ( info->xmit_buf )
	    free_page(page);
	else
	    info->xmit_buf = (unsigned char *)page;

	/*
	 * Clear the FIFO buffers and disable them
	 * (they will be reenabled in mxser_change_speed())
	 */
	if ( info->xmit_fifo_size == 16 )
	    outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT),
		 info->base + UART_FCR);

	/*
	 * At this point there's no way the LSR could still be 0xFF;
	 * if it is, then bail out, because there's likely no UART
	 * here.
	 */
	if ( inb(info->base + UART_LSR) == 0xff ) {
	    restore_flags(flags);
	    if ( suser() ) {
		if ( info->tty )
		    set_bit(TTY_IO_ERROR, &info->tty->flags);
		return(0);
	    } else
		return(-ENODEV);
	}
         
	/*
	 * Clear the interrupt registers.
	 */
	(void)inb(info->base + UART_LSR);
	(void)inb(info->base + UART_RX);
	(void)inb(info->base + UART_IIR);
	(void)inb(info->base + UART_MSR);

	/*
	 * Now, initialize the UART
	 */
	outb(UART_LCR_WLEN8, info->base + UART_LCR);	/* reset DLAB */
	info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;/*validation des signaux DTR RTS + Validation des it*/
	outb(info->MCR, info->base + UART_MCR);

	/*
	 * Finally, enable interrupts
	 */
	info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
	outb(info->IER, info->base + UART_IER); /* enable interrupts */

	/*
	 * And clear the interrupt registers again for luck.
	 */
	(void)inb(info->base + UART_LSR);
	(void)inb(info->base + UART_RX);
	(void)inb(info->base + UART_IIR);
	(void)inb(info->base + UART_MSR);

#if (LINUX_VERSION_CODE <  VERSION_CODE(2,1,0))
	if ( info->tty )
	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
#else
	if ( info->tty )
	    test_and_clear_bit(TTY_IO_ERROR, &info->tty->flags);
#endif
	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;

	/*
	 * and set the speed of the serial port
	 */
	mxser_change_speed(info, 0);

	info->flags |= ASYNC_INITIALIZED;
	restore_flags(flags);
	return(0);
}

/*
 * This routine will shutdown a serial port; interrupts maybe disabled, and
 * DTR is dropped if the hangup on close termio flag is on.
 */
static void mxser_shutdown(struct mxser_struct * info)
{
	unsigned long	flags;

	if ( !(info->flags & ASYNC_INITIALIZED) )
	    return;

	save_flags(flags);
	cli();			/* Disable interrupts */

	/*
	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
	 * here so the queue might never be waken up
	 */
	wake_up_interruptible(&info->delta_msr_wait);

	/*
	 * Free the IRQ, if necessary
	 */
	if ( info->xmit_buf ) {
	    free_page((unsigned long)info->xmit_buf);
	    info->xmit_buf = 0;
	}

	info->IER = 0;
	outb(0x00, info->base + UART_IER);	/* disable all intrs */

	if ( !info->tty || (info->tty->termios->c_cflag & HUPCL) )
	    info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS);
	outb(info->MCR, info->base + UART_MCR);

	/* clear Rx/Tx FIFO's */
	outb((UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), info->base + UART_FCR);
	/* read data port to reset things */
	(void)inb(info->base + UART_RX);

	if ( info->tty )
	    set_bit(TTY_IO_ERROR, &info->tty->flags);

	info->flags &= ~ASYNC_INITIALIZED;
	restore_flags(flags);
}

/*
 * This routine is called to set the UART divisor registers to match
 * the specified baud rate for a serial port.
 */
static int mxser_change_speed(struct mxser_struct *info,
                              struct termios *old_termios)
{
	int		quot = 0;
	unsigned	cflag, cval, fcr;
	int		i;
        int             ret = 0;
	unsigned char Byte;
	
	if ( !info->tty || !info->tty->termios )
	    return ret;
	cflag = info->tty->termios->c_cflag;
	if ( !(info->base) )
	    return ret;

#ifndef B921600
#define B921600 (B460800 +1)
#endif 
	switch( cflag & (CBAUD | CBAUDEX) ){
        case B921600 : i = 20; break;
        case B460800 : i = 19; break;
        case B230400 : i = 18; break;
        case B115200 : i = 17; break;
        case B57600 : i = 16; break;
        case B38400 : i = 15; break;
        case B19200 : i = 14; break;
        case B9600 : i = 13; break;
        case B4800 : i = 12; break;
        case B2400 : i = 11; break;
        case B1800 : i = 10; break;
        case B1200 : i = 9; break;
        case B600 : i = 8; break;
        case B300 : i = 7; break;
        case B200 : i = 6; break;
        case B150 : i = 5; break;
        case B134 : i = 4; break;
        case B110 : i = 3; break;
        case B75 : i = 2; break;
        case B50 : i = 1; break;
        default:   i = 0; break;
	}
	
	if ( i == 15 ) {
	    if ( (info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI )
		i = 16; 					/* 57600 bps */
	    if ( (info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI )
		i = 17; 					/* 115200 bps */
                
#ifdef ASYNC_SPD_SHI
	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
                i = 18;
#endif

#ifdef ASYNC_SPD_WARP
            if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
                i = 19;
#endif
	}
	if ( mxvar_baud_table[i] == 134 ) {
	    quot = (2 * info->baud_base / 269);
	} else if ( mxvar_baud_table[i] ) {
	    quot = info->baud_base / mxvar_baud_table[i];
       	    if (!quot && old_termios) {
                /* re-calculate */
        	info->tty->termios->c_cflag &= ~CBAUD;
        	info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
		switch( info->tty->termios->c_cflag & (CBAUD | CBAUDEX) ){
	        case B921600 : i = 20; break;
        	case B460800 : i = 19; break;
	        case B230400 : i = 18; break;
        	case B115200 : i = 17; break;
	        case B57600 : i = 16; break;
        	case B38400 : i = 15; break;
	        case B19200 : i = 14; break;
        	case B9600 : i = 13; break;
	        case B4800 : i = 12; break;
        	case B2400 : i = 11; break;
	        case B1800 : i = 10; break;
        	case B1200 : i = 9; break;
	        case B600 : i = 8; break;
        	case B300 : i = 7; break;
	        case B200 : i = 6; break;
        	case B150 : i = 5; break;
	        case B134 : i = 4; break;
        	case B110 : i = 3; break;
	        case B75 : i = 2; break;
        	case B50 : i = 1; break;
	        default:   i = 0; break;
		}
        	if ( i == 15 ) {
        	    if ( (info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI )
        	        i = 16; 					/* 57600 bps */
        	    if ( (info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI )
        	        i = 17; 					/* 115200 bps */
#ifdef ASYNC_SPD_SHI
	            if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
                        i = 18;
#endif
#ifdef ASYNC_SPD_WARP
                    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
                        i = 19;
#endif
        	}
        	if ( mxvar_baud_table[i] == 134 ) {
        	    quot = (2 * info->baud_base / 269);
                } else if ( mxvar_baud_table[i] ) {
        	    quot = info->baud_base / mxvar_baud_table[i];
                    if(quot==0)
                        quot = 1;
        	} else {
        	    quot = 0;
        	}
            }else if(quot==0)
                quot = 1;
	} else {
	    quot = 0;
	}
	if ( quot ) {
	    info->MCR |= UART_MCR_DTR;
	    cli();
	    outb(info->MCR, info->base + UART_MCR);
	    sti();
	} else {
	    info->MCR &= ~UART_MCR_DTR;
	    cli();
	    outb(info->MCR, info->base + UART_MCR);
	    sti();
	    return ret;
	}
	/* byte size and parity */
	switch ( cflag & CSIZE ) {
	case CS5: cval = 0x00; break;
	case CS6: cval = 0x01; break;
	case CS7: cval = 0x02; break;
	case CS8: cval = 0x03; break;
	default:  cval = 0x00; break;	/* too keep GCC shut... */
	}
	if ( cflag & CSTOPB )
	    cval |= 0x04;
	if ( cflag & PARENB )
	    cval |= UART_LCR_PARITY;
	if ( !(cflag & PARODD) )
	    cval |= UART_LCR_EPAR;
	if ( (info->type == PORT_8250) || (info->type == PORT_16450) ) {
	    fcr = 0;
	} else {
	    fcr = UART_FCR_ENABLE_FIFO;
	    switch ( info->rx_trigger ) {
	    case 1:  fcr |= UART_FCR_TRIGGER_1; break;
	    case 4:  fcr |= UART_FCR_TRIGGER_4; break;
	    case 8:  fcr |= UART_FCR_TRIGGER_8; break;
	    default: fcr |= UART_FCR_TRIGGER_14;
	    }
	}
	
	/* CTS flow control flag and modem status interrupts */
	info->IER &= ~UART_IER_MSI;
	info->MCR &= ~UART_MCR_AFE;
	if ( cflag & CRTSCTS ) {
	    info->flags |= ASYNC_CTS_FLOW;
	    info->IER |= UART_IER_MSI;

		switch ( info->type)
		{
			case PORT_16550A : 
				info->MCR |= UART_MCR_AFE;
			break;
			
			case PORT_OX954 : 
				Byte = OxserRead650(info->base,UART_EFR);
				Byte |=(UART_EFR_CTS | UART_EFR_RTS);
				OxserWrite650(info->base,UART_EFR, Byte );			
			break;
		}
	} else {
	    info->flags &= ~ASYNC_CTS_FLOW;
		if(info->type == PORT_OX954)
		{
			Byte = OxserRead650(info->base,UART_EFR);
			Byte &= (~UART_EFR_CTS & ~UART_EFR_RTS);
            OxserWrite650(info->base,UART_EFR, Byte);
		}
	}
	outb(info->MCR, info->base + UART_MCR);
	if ( cflag & CLOCAL )
	    info->flags &= ~ASYNC_CHECK_CD;
	else {
	    info->flags |= ASYNC_CHECK_CD;
	    info->IER |= UART_IER_MSI;
	}
	outb(info->IER, info->base + UART_IER);

	/*
	 * Set up parity check flag
	 */
	info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
	if ( I_INPCK(info->tty) )
	    info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
	if ( I_BRKINT(info->tty) || I_PARMRK(info->tty) )
	    info->read_status_mask |= UART_LSR_BI;

	info->ignore_status_mask = 0;
#if 0
	/* This should be safe, but for some broken bits of hardware... */
	if ( I_IGNPAR(info->tty) ) {
	    info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
	    info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
	}
#endif
	if ( I_IGNBRK(info->tty) ) {
	    info->ignore_status_mask |= UART_LSR_BI;
	    info->read_status_mask |= UART_LSR_BI;
	    /*
	     * If we're ignore parity and break indicators, ignore
	     * overruns too.  (For real raw support).
	     */
	    if ( I_IGNPAR(info->tty) ) {
		info->ignore_status_mask |= UART_LSR_OE|UART_LSR_PE|UART_LSR_FE;
		info->read_status_mask |= UART_LSR_OE|UART_LSR_PE|UART_LSR_FE;
	    }
	}
	cli();
	outb(cval | UART_LCR_DLAB, info->base + UART_LCR);  /* set DLAB */
	outb(quot & 0xff, info->base + UART_DLL);	    /* LS of divisor */
	outb(quot >> 8, info->base + UART_DLM); 	    /* MS of divisor */
	outb(cval, info->base + UART_LCR);		    /* reset DLAB */
	outb(fcr, info->base + UART_FCR);		    /* set fcr */
	sti();

        return ret;
}

/*
 * ------------------------------------------------------------
 * friends of mxser_ioctl()
 * ------------------------------------------------------------
 */
static int mxser_get_serial_info(struct mxser_struct * 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 = info->base;
	tmp.irq = info->irq;
	tmp.flags = info->flags;
	tmp.baud_base = info->baud_base;
	tmp.close_delay = info->close_delay;
	tmp.closing_wait = info->closing_wait;
	tmp.custom_divisor = info->custom_divisor;
	tmp.hub6 = 0;
	copy_to_user(retinfo, &tmp, sizeof(*retinfo));
	return(0);
}

static int mxser_set_serial_info(struct mxser_struct * info,
				 struct serial_struct * new_info)
{
	struct serial_struct	new_serial;
	unsigned int		flags;
	int			retval = 0;

	if ( !new_info || !info->base )
	    return(-EFAULT);
	copy_from_user(&new_serial, new_info, sizeof(new_serial));

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

	flags = info->flags & ASYNC_SPD_MASK;

	if ( !suser() ) {
	    if ( (new_serial.baud_base != info->baud_base) ||
		 (new_serial.close_delay != info->close_delay) ||
		 ((new_serial.flags & ~ASYNC_USR_MASK) !=
		 (info->flags & ~ASYNC_USR_MASK)) )
		return(-EPERM);
	    info->flags = ((info->flags & ~ASYNC_USR_MASK) |
			  (new_serial.flags & ASYNC_USR_MASK));
	} else {
	    /*
	     * OK, past this point, all the error checking has been done.
	     * At this point, we start making changes.....
	     */
	    info->flags = ((info->flags & ~ASYNC_FLAGS) |
			  (new_serial.flags & ASYNC_FLAGS));
	    info->close_delay = new_serial.close_delay * HZ/100;
	    info->closing_wait = new_serial.closing_wait * HZ/100;
	}

	if ( info->flags & ASYNC_INITIALIZED ) {
	    if ( flags != (info->flags & ASYNC_SPD_MASK) ){
		mxser_change_speed(info,0);
	    }
	} else
	    retval = mxser_startup(info);
	return(retval);
}

/*
 * mxser_get_lsr_info - get line status register info
 *
 * Purpose: Let user call ioctl() to get info when the UART physically
 *	    is emptied.  On bus types like RS485, the transmitter must
 *	    release the bus after transmitting. This must be done when
 *	    the transmit shift register is empty, not be done when the
 *	    transmit holding register is empty.  This functionality
 *	    allows an RS485 driver to be written in user space.
 */
static int mxser_get_lsr_info(struct mxser_struct * info, unsigned int *value)
{
	unsigned char	status;
	unsigned int	result;

	cli();
	status = inb(info->base + UART_LSR);
	sti();
	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
	put_to_user(result, value);
	return(0);
}

/*
 * This routine sends a break character out the serial port.
 */
static void mxser_send_break(struct mxser_struct * info, int duration)
{
	if ( !info->base )
	    return;
	current->state = TASK_INTERRUPTIBLE;
	cli();
	outb(inb(info->base + UART_LCR) | UART_LCR_SBC, info->base + UART_LCR);
	schedule_timeout(duration);
	outb(inb(info->base + UART_LCR) & ~UART_LCR_SBC, info->base + UART_LCR);
	sti();
}

static int mxser_get_modem_info(struct mxser_struct * info,
				unsigned int *value)
{
	unsigned char	control, status;
	unsigned int	result;

	control = info->MCR;
	cli();
	status = inb(info->base + UART_MSR);
	if ( status & UART_MSR_ANY_DELTA )
	    mxser_check_modem_status(info, status);
	sti();
	result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) |
		 ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) |
		 ((status  & UART_MSR_DCD) ? TIOCM_CAR : 0) |
		 ((status  & UART_MSR_RI)  ? TIOCM_RNG : 0) |
		 ((status  & UART_MSR_DSR) ? TIOCM_DSR : 0) |
		 ((status  & UART_MSR_CTS) ? TIOCM_CTS : 0);
	put_to_user(result, value);
	return(0);
}

static int mxser_set_modem_info(struct mxser_struct * info, unsigned int cmd,
				unsigned int *value)
{
	int		error;
	unsigned int	arg;

	error = verify_area(VERIFY_READ, value, sizeof(int));
	if ( error )
	    return(error);
	get_from_user(arg,value);
	switch ( cmd ) {
	case TIOCMBIS:
	    if ( arg & TIOCM_RTS )
		info->MCR |= UART_MCR_RTS;
	    if ( arg & TIOCM_DTR )
		info->MCR |= UART_MCR_DTR;
	    break;
	case TIOCMBIC:
	    if ( arg & TIOCM_RTS )
		info->MCR &= ~UART_MCR_RTS;
	    if ( arg & TIOCM_DTR )
		info->MCR &= ~UART_MCR_DTR;
	    break;
	case TIOCMSET:
	    info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR)) |
			((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) |
			((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
	    break;
	default:
	    return(-EINVAL);
	}
	cli();
	outb(info->MCR, info->base + UART_MCR);
	sti();
	return(0);
}
