// **********************************************************************
// * serial22.c                                                         *
// *                                                                    *
// * Written by Matthew DeLoera (deloera@us.ibm.com)                    *
// * (C) Copyright IBM Corporation, 1998-2001                           *
// *                                                                    *
// *  This software may be used and distributed according to the terms  *
// *  of the GNU Public License, incorporated herein by reference.      *
// **********************************************************************

// **********************************************************************
// * serial22.c - This is the 2.2 kernel version of the serial driver.  *
// *              This driver is based on the standard Linux serial     *
// *              driver, with the following copyright information :    *
// *                                                                    *
// *  linux/drivers/char/serial.c                                       *
// *                                                                    *
// *  Copyright (C) 1991, 1992  Linus Torvalds                          *
// *                                                                    *
// *  Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92.  Now  *
// *  much more extensible to support other serial cards based on the   *
// *  16450/16550A UART's.  Added support for the AST FourPort and the  *
// *  Accent Async board.                                               *
// **********************************************************************


// ************
// * INCLUDES *
// ************

#include <linux/autoconf.h>
#ifdef CONFIG_SMP
     #define __SMP__
#endif
#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
     #define MODVERSIONS
#endif
#ifdef MODVERSIONS
     #include <linux/modversions.h>
#endif
#include <linux/config.h>
#include <linux/module.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/malloc.h>
#include <linux/slab.h>                   // PHR_180252
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/serial.h>
// #ifndef LINUX_VERSION_CODE
//     #include <linux/version.h>
//#endif


#define _INLINE_ inline
	
static char   *serial_version = "4.27";
static        DECLARE_TASK_QUEUE(tq_serial);
static struct tty_driver serial_driver;
static int    serial_refcount;

#define WAKEUP_CHARS 256 /* number of characters left in xmit buffer before we ask for more */

static struct async_struct *IRQ_ports[NR_IRQS];
static int                 IRQ_timeout[NR_IRQS]; // Max timeout for each IRQ after the IRQ has been active.

// PROTOTYPES
static void autoconfig(struct serial_state * info);
static void change_speed(struct async_struct *info, struct termios *old);
static void rs_wait_until_sent(struct tty_struct *tty, int timeout);


// Here we define the default xmit fifo size used for each type of UART
static struct serial_uart_config uart_config[] = {
	{ "unknown",    1, 0 }, 
	{ "8250",       1, 0 }, 
	{ "16450",      1, 0 }, 
	{ "16550",      1, 0 }, 
	{ "16550A",    16, UART_CLEAR_FIFO | UART_USE_FIFO }, 
	{ "cirrus",     1, 0 }, 
	{ "ST16650",    1, UART_CLEAR_FIFO | UART_STARTECH }, 
	{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, 
	{ "TI16750",   64, UART_CLEAR_FIFO | UART_USE_FIFO},
	{ 0, 0}
};

static struct serial_state rs_table[] = {
	SERIAL_PORT_DFNS	/* Defined in serial.h */
};

#define NR_PORTS	1
static struct tty_struct *serial_table[NR_PORTS];
static struct termios    *serial_termios[NR_PORTS];
static struct termios    *serial_termios_locked[NR_PORTS];

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


// ******************************************************************************************************************************************
// * tmp_buf is used as a temporary buffer by serial_write.  We need to lock it in case the copy_from_user 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 *tmp_buf;
static struct semaphore tmp_buf_sem = MUTEX;


// ***************************************
// * IBM-SPECIFIC ROUTINES AND VARIABLES *
// ***************************************

extern unsigned short IBMirq;
extern unsigned short IBMport;
extern unsigned char  IBMASMibmasmexists;
extern unsigned char  IBMASMwisemanexists;
extern unsigned char  IBMASMeagleexists;
unsigned char         IBM_devid = 0;
extern void           IBM_GetDeviceInformation (void);
extern void           IBM_DisableInterrupts    (void);
extern void           IBM_EnableInterrupts     (void);
extern unsigned char  IBM_IsSerialInterrupt    (void);
extern void           IBM_WriteEagleUART       (unsigned char offset, unsigned char value);
extern unsigned char  IBM_ReadEagleUART        (unsigned char offset);
struct  timer_list    UARTAccessTimer;  // RJ01
int                   Special_Flag = 1; //RJ01
void                  RecheckUartOwnership(struct timer_list *existing_timer); //RJ01
int                   Host_Flag_On_Startup=0; //RJ01

// ********************************************************************
// * THESE ROUTINES ENCAPSULATE COMMUNICATION WITH THE UART REGISTERS *
// ********************************************************************

static inline unsigned int serial_in(struct async_struct *info, int offset)
{
     if (IBMASMeagleexists) return IBM_ReadEagleUART(offset);
     return inb(info->port + offset);
}


static inline unsigned int serial_inp(struct async_struct *info, int offset)
{
     if (IBMASMeagleexists) return IBM_ReadEagleUART(offset);
     return inb(info->port + offset);
}


static inline void serial_out(struct async_struct *info, int offset, int value)
{

    if (Host_Flag_On_Startup == 0) 
    {
        //Not Allowed to write to UART regs
        //Linux serial driver initializes UART at different places
        //while loading. This is the best place to avoid clobbering of UART regs
        //if the driver does not own the UART.

        return;
    }

     if (IBMASMeagleexists) IBM_WriteEagleUART(offset, value);
     else outb(value, info->port+offset);
     return;
}


static inline void serial_outp(struct async_struct *info, int offset, int value)
{
     if (IBMASMeagleexists) IBM_WriteEagleUART(offset, value);
     else outb(value, info->port+offset);
     return;
}


// **********************************************************************
// * rs_stop() and rs_start()                                           *
// *                                                                    *
// * This routines are called before setting or resetting tty->stopped. *
// * They enable or disable transmitter interrupts, as necessary.       *
// **********************************************************************

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

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


static void rs_start(struct tty_struct *tty)
{
     struct async_struct *info = (struct async_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;
          serial_out(info, UART_IER, info->IER);
     }
     restore_flags(flags);
}


// **********************************************************************
// * Here starts the interrupt handling routines.  All of the following *
// * subroutines are declared as inline and are folded into             *
// * rs_interrupt().  They were separated out for readability's sake.   *
// *                                                                    *
// * Note: rs_interrupt() is a "fast" interrupt, which means that it    *
// * runs with interrupts turned off.  People who may want to modify    *
// * rs_interrupt() should try to keep the interrupt handler as fast as *
// * possible.                                                          *
// *                                                                    *
// * rs_sched_event() is used by the interrupt handler to schedule      *
// * processing in the software interrupt portion of the driver.        *
// **********************************************************************

static _INLINE_ void rs_sched_event(struct async_struct *info, int event)
{
     info->event |= 1 << event;
     queue_task(&info->tqueue, &tq_serial);
     mark_bh(SERIAL_BH);
}


static _INLINE_ void receive_chars(struct async_struct *info, int *status)
{
     struct tty_struct   *tty = info->tty;
     unsigned char       ch;
     int                 ignored = 0;
     struct async_icount *icount;

     icount = &info->state->icount;
     do
     {
          ch = serial_inp(info, UART_RX);
          if (tty->flip.count >= TTY_FLIPBUF_SIZE) break;
          *tty->flip.char_buf_ptr = ch;
          icount->rx++;
		
          *tty->flip.flag_buf_ptr = 0;
          if (*status & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE | UART_LSR_OE))
          {
               // FOR STATISTICS ONLY
               if (*status & UART_LSR_BI)
               {
                    *status &= ~(UART_LSR_FE | UART_LSR_PE);
                    icount->brk++;
               }
               else if (*status & UART_LSR_PE) icount->parity++;
               else if (*status & UART_LSR_FE) icount->frame++;
               if (*status & UART_LSR_OE) icount->overrun++;

               // Now check to see if character should be ignored, and mask off
               // conditions which should be ignored.
               if (*status & info->ignore_status_mask)
               {
                    if (++ignored > 100) break;
                    goto ignore_char;
               }
               *status &= info->read_status_mask;
		
               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;
               if (*status & UART_LSR_OE)
               {
                    // Overrun is special, since it's reported immediately, and
                    // doesn't affect the current character
                    if (tty->flip.count < TTY_FLIPBUF_SIZE)
                    {
                         tty->flip.count++;
                         tty->flip.flag_buf_ptr++;
                         tty->flip.char_buf_ptr++;
                         *tty->flip.flag_buf_ptr = TTY_OVERRUN;
                    }
               }
          }
          tty->flip.flag_buf_ptr++;
          tty->flip.char_buf_ptr++;
          tty->flip.count++;
          ignore_char: *status = serial_inp(info, UART_LSR);
     } while (*status & UART_LSR_DR);
     tty_flip_buffer_push(tty);
}


static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
{
     int count;
	
     if (info->x_char)
     {
          serial_outp(info, UART_TX, info->x_char);
          info->state->icount.tx++;
          info->x_char = 0;
          if (intr_done) *intr_done = 0;
          return;
     }
     if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tty->hw_stopped)
     {
          info->IER &= ~UART_IER_THRI;
          serial_out(info, UART_IER, info->IER);
          return;
     }
	
     count = info->xmit_fifo_size;
     do
     {
          serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
          info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
          info->state->icount.tx++;
          if (--info->xmit_cnt <= 0) break;
     } while (--count > 0);
	
     if (info->xmit_cnt < WAKEUP_CHARS) rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);

     if (intr_done) *intr_done = 0;

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


static _INLINE_ void check_modem_status(struct async_struct *info)
{
     int    status;
     struct async_icount *icount;
	
     status = serial_in(info, UART_MSR);

     if (status & UART_MSR_ANY_DELTA)
     {
          icount = &info->state->icount;

          /* update input line counters */
          if (status & UART_MSR_TERI) icount->rng++;
          if (status & UART_MSR_DDSR) icount->dsr++;
          if (status & UART_MSR_DDCD)
          {
               icount->dcd++;
          }
          if (status & UART_MSR_DCTS) 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)))
          {
               if (info->tty) tty_hangup(info->tty);
          }
     }
     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;
                    serial_out(info, UART_IER, info->IER);
                    rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
                    return;
               }
          }
          else
          {
               if (!(status & UART_MSR_CTS))
               {
                    info->tty->hw_stopped = 1;
                    info->IER &= ~UART_IER_THRI;
                    serial_out(info, UART_IER, info->IER);
               }
          }
     }
}


// *******************************************************************
// * This is the serial driver's interrupt routine for a single port *
// *******************************************************************
 
static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs *regs)
{
     int status;
     int pass_counter = 0;
     struct async_struct * info;
     unsigned char host_flag; //RJ01
     int interrupt_id; //RJ01


     /*RJ01 Begin */
     if ( ( Special_Flag == 1) )
     {
        /*Host flag indicates that serial port is unavailable for use by host. */
        //printk(KERN_CRIT "ISR : Special_Flag is 1, returning.\n"); //RJ01
         return ;
     }
     /*RJ01 End */
	
     // ENSURE THIS INTERRUPT IS FOR US
     if (!IBM_IsSerialInterrupt()) return;
	
     info = IRQ_ports[irq];
     if (!info || !info->tty) return;

     do
     {
          status = serial_inp(info, UART_LSR);
          if (status & UART_LSR_DR) receive_chars(info, &status);
          check_modem_status(info);
          if (status & UART_LSR_THRE) transmit_chars(info, 0);
          if (pass_counter++ > 256) break;

          interrupt_id = serial_in(info, UART_IIR); //RJ01
          if (interrupt_id == UART_IIR_MSI) 
          {
             // Modem Status Change interurpt detected
              host_flag = IBM_QueryHostFlag();

              if (host_flag == 0) 
              {
                 Special_Flag = 1;

                 //Disable PCI COM interrupt
                 IBM_DisableInterrupts();

                 //start finite timer
                 del_timer(&(UARTAccessTimer)); //in case already running
                 UARTAccessTimer.function = (void *)RecheckUartOwnership;
                 UARTAccessTimer.data     = (unsigned long)&UARTAccessTimer;
                 UARTAccessTimer.expires  = jiffies + 10 * HZ; // 10 second callback
                 add_timer(&(UARTAccessTimer));

                 //return from ISR indicating that interrupt was handled
                 return;
              }


          }

     } while (!(interrupt_id & UART_IIR_NO_INT));
     info->last_active = jiffies;
}


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

void RecheckUartOwnership(struct timer_list *existing_timer)
{

    unsigned char host_flag; 

     //printk(KERN_CRIT "RecheckUartOwnership: Entry.\n");

     if (Special_Flag == 0) 
     {
        return; // should never get here since timer is only started when Special_Flag is true
     }

     // Special_Flag is true
      host_flag = IBM_QueryHostFlag();

     if (host_flag == 0) 
     {
        //Restart the finite timer and return
        UARTAccessTimer.function = (void *)RecheckUartOwnership;
        UARTAccessTimer.data     = (unsigned long)&UARTAccessTimer;
        UARTAccessTimer.expires  = jiffies + 10 * HZ; // 10 second callback
        add_timer(&(UARTAccessTimer));
        return;
     }
     else
     {
         Special_Flag = 0;

         //Enable COM PCI interrupt
         IBM_EnableInterrupts();
     }


     return;
}

// ***********************************************************************
// * This routine is used to handle the "bottom half" processing for the *
// * serial driver, known also the "software interrupt" processing.      *
// * This processing is done at the kernel interrupt level, after the    *
// * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This   *
// * is where time-consuming activities which can not be done in the     *
// * interrupt driver proper are done; the interrupt driver schedules    *
// * them using rs_sched_event(), and they get done here.                *
// ***********************************************************************

static void do_serial_bh(void)
{
     run_task_queue(&tq_serial);
}


static void do_softint(void *private_)
{
     struct async_struct *info = (struct async_struct *)private_;
     struct tty_struct   *tty;
	
     tty = info->tty;
     if (!tty) return;

     if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &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);
     }
}


// ***********************************************************************
// * Low level utility subroutines for the serial driver:  routines to   *
// * figure out the appropriate timeout for an interrupt chain, routines *
// * to initialize and startup a serial port, and routines to shutdown a *
// * serial port.  Useful stuff like that.                               *
// *                                                                     *
// * This routine figures out the correct timeout for a particular IRQ.  *
// * It uses the smallest timeout of all of the serial ports in a        *
// * particular interrupt chain.  Now only used for IRQ 0....            *
// ***********************************************************************

static void figure_IRQ_timeout(int irq)
{
     struct async_struct *info;
     int    timeout = 60 * HZ;    /* 60 seconds === a long time :-) */

     info = IRQ_ports[irq];
     if (!info)
     {
          IRQ_timeout[irq] = 60*HZ;
          return;
     }
     while (info)
     {
          if (info->timeout < timeout) timeout = info->timeout;
          info = info->next_port;
     }
     if (!irq) timeout = timeout / 2;
     IRQ_timeout[irq] = timeout ? timeout : 1;
}


static int startup(struct async_struct * info)
{
     unsigned long flags;
     int retval=0;
     void (*handler)(int, void *, struct pt_regs *);
     struct serial_state *state= info->state;
     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);
          goto errout;
     }

     if (!state->port || !state->type)
     {
          if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
          free_page(page);
          goto errout;
     }
     if (info->xmit_buf) free_page(page);
     else info->xmit_buf = (unsigned char *) page;

     if (uart_config[info->state->type].flags & UART_STARTECH)
     {
          /* Wake up UART */
          serial_outp(info, UART_LCR, 0xBF);
          serial_outp(info, UART_EFR, UART_EFR_ECB);
          serial_outp(info, UART_IER, 0);
          serial_outp(info, UART_EFR, 0);
          serial_outp(info, UART_LCR, 0);
     }

     if (info->state->type == PORT_16750)
     {
          /* Wake up UART */
          serial_outp(info, UART_IER, 0);
     }

     // Clear the FIFO buffers and disable them (they will be reenabled in change_speed())
     if (uart_config[state->type].flags & UART_CLEAR_FIFO)
          serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));

     // 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 (serial_inp(info, UART_LSR) == 0xff)
     {
          if (capable(CAP_SYS_ADMIN))
          {
               if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
          }
          else retval = -ENODEV;
          goto errout;
     }
	
     // Allocate the IRQ if necessary
     if (state->irq && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port))
     {
          if (IRQ_ports[state->irq])
          {
               retval = -EBUSY;
               goto errout;
          }
          else handler = rs_interrupt_single;

          retval = request_irq(state->irq, rs_interrupt_single, SA_SHIRQ, "ibmser", &IBM_devid);
          if (retval)
          {
               if (capable(CAP_SYS_ADMIN))
               {
                    if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
                    retval = 0;
               }
               goto errout;
          }
          IBM_EnableInterrupts();
     }

     // Insert serial port into IRQ chain.
     info->prev_port = 0;
     info->next_port = IRQ_ports[state->irq];
     if (info->next_port) info->next_port->prev_port = info;
     IRQ_ports[state->irq] = info;
     figure_IRQ_timeout(state->irq);

     // Clear the interrupt registers.
     /* (void) serial_inp(info, UART_LSR); */   /* (see above) */
     (void) serial_inp(info, UART_RX);
     (void) serial_inp(info, UART_IIR);
     (void) serial_inp(info, UART_MSR);

     // Now, initialize the UART 
     serial_outp(info, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */

     info->MCR = 0;
     if (info->tty->termios->c_cflag & CBAUD) info->MCR = UART_MCR_DTR | UART_MCR_RTS;
     {
          if (state->irq != 0) info->MCR |= UART_MCR_OUT2;
     }
     serial_outp(info, UART_MCR, info->MCR);
	
     // Finally, enable interrupts
     info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
     serial_outp(info, UART_IER, info->IER);	/* enable interrupts */
	
     // And clear the interrupt registers again for luck.
     (void)serial_inp(info, UART_LSR);
     (void)serial_inp(info, UART_RX);
     (void)serial_inp(info, UART_IIR);
     (void)serial_inp(info, UART_MSR);

     if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags);
     info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;

     // Set up the tty->alt_speed kludge and set the speed of the serial port
     if (info->tty)
     {
          if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)   info->tty->alt_speed = 57600;
          if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)  info->tty->alt_speed = 115200;
          if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)  info->tty->alt_speed = 230400;
          if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800;
     }
     change_speed(info, 0);

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


// This routine will shutdown a serial port; interrupts are disabled, and DTR
// is dropped if the hangup on close termio flag is on.

static void shutdown(struct async_struct * info)
{
     unsigned long       flags;
     struct serial_state *state;
     int                 retval;

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

     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);
	
     // First unlink the serial port from the IRQ chain...
     if (info->next_port) info->next_port->prev_port = info->prev_port;
     if (info->prev_port) info->prev_port->next_port = info->next_port;
     else                 IRQ_ports[state->irq] = info->next_port;
     figure_IRQ_timeout(state->irq);
	
     // Free the IRQ, if necessary
     if (state->irq && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port))
     {
          if (IRQ_ports[state->irq])
          {
               free_irq(state->irq, (void*)&IBM_devid);
               retval = request_irq(state->irq, rs_interrupt_single, SA_SHIRQ, "ibmser", (void*)&IBM_devid);
               if (retval) printk("serial shutdown: request_irq: error %d\nCouldn't reacquire IRQ.\n", retval);
               IBM_EnableInterrupts();
          }
          else
          {
               free_irq(state->irq, (void*)&IBM_devid);
          }
     }

     if (info->xmit_buf)
     {
          free_page((unsigned long) info->xmit_buf);
          info->xmit_buf = 0;
     }

     info->IER = 0;
     serial_outp(info, UART_IER, 0x00);	/* disable all intrs */
     info->MCR &= ~UART_MCR_OUT2;
	
     /* disable break condition */
     serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
	
     if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
     serial_outp(info, UART_MCR, info->MCR);

     /* disable FIFO's */	
     serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
     (void)serial_in(info, UART_RX);    /* read data port to reset things */
	
     if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
     if (uart_config[info->state->type].flags & UART_STARTECH)
     {
          /* Arrange to enter sleep mode */
          serial_outp(info, UART_LCR, 0xBF);
          serial_outp(info, UART_EFR, UART_EFR_ECB);
          serial_outp(info, UART_IER, UART_IERX_SLEEP);
          serial_outp(info, UART_LCR, 0);
     }
     if (info->state->type == PORT_16750)
     {
          /* Arrange to enter sleep mode */
          serial_outp(info, UART_IER, UART_IERX_SLEEP);
     }
     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 void change_speed(struct async_struct *info, struct termios *old_termios)
{
     unsigned short port;
     int quot = 0, baud_base, baud;
     unsigned cflag, cval, fcr = 0;
     int bits;
     unsigned long flags;

     if (!info->tty || !info->tty->termios) return;
     cflag = info->tty->termios->c_cflag;
     if (!(port = info->port)) return;

     /* byte size and parity */
     switch (cflag & CSIZE)
     {
          case CS5: cval = 0x00;
                    bits = 7;
                    break;
          case CS6: cval = 0x01;
                    bits = 8;
                    break;
          case CS7: cval = 0x02;
                    bits = 9;
                    break;
          case CS8: cval = 0x03;
                    bits = 10;
                    break;
          /* Never happens, but GCC is too dumb to figure it out */
          default:  cval = 0x00;
                    bits = 7;
                    break;
     }
     if (cflag & CSTOPB)
     {
          cval |= 0x04;
          bits++;
     }
     if (cflag & PARENB)
     {
          cval |= UART_LCR_PARITY;
          bits++;
     }
     if (!(cflag & PARODD)) cval |= UART_LCR_EPAR;

     /* Determine divisor based on baud rate */
     baud = tty_get_baud_rate(info->tty);
     baud_base = info->state->baud_base;

     // MMDBAUD - Replacing the baud value with something translated for Wiseman
     if      (baud == 50)     quot = 0x2400;  // 50
     else if (baud == 75)     quot = 0x1800;  // 75
     else if (baud == 110)    quot = 0x105d;  // 110
     else if (baud == 134)    quot = 0x0d62;  // 134.5
     else if (baud == 150)    quot = 0x0c00;  // 150
     else if (baud == 300)    quot = 0x0600;  // 300
     else if (baud == 600)    quot = 0x0300;  // 600
     else if (baud == 1200)   quot = 0x0180;  // 1200
     else if (baud == 1800)   quot = 0x0100;  // 1800
     else if (baud == 2000)   quot = 0x00e6;  // 2000
     else if (baud == 2400)   quot = 0x00c0;  // 2400
     else if (baud == 3600)   quot = 0x0080;  // 3600
     else if (baud == 4800)   quot = 0x0060;  // 4800
     else if (baud == 7200)   quot = 0x0040;  // 7200
     else if (baud == 9600)   quot = 0x0030;  // 9600
     else if (baud == 19200)  quot = 0x0018;  // 19200
     else if (baud == 38400)  quot = 0x000c;  // 38400
     else if (baud == 57600)  quot = 0x0008;  // 57600
     else if (baud == 115200) quot = 0x0004;  // 115200
     else
     {
          // OTHERWISE DEFAULT TO 9600 BAUD
          baud = 9600;
          quot = 0x0030;
     }
     if (IBMASMeagleexists) quot = quot / 2;

     // REMAINING SETUP, BAUD-WISE
     info->quot    = quot;
     info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
     info->timeout += HZ / 50;     /* Add .02 seconds of slop */

     /* Set up FIFO's */
     if (uart_config[info->state->type].flags & UART_USE_FIFO)
     {
          if ((info->state->baud_base / quot) < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
          else                                        fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
     }
     if (info->state->type == PORT_16750) fcr |= UART_FCR7_64BYTE;
	
     /* CTS flow control flag and modem status interrupts */
     info->IER &= ~UART_IER_MSI;
     if (info->flags & ASYNC_HARDPPS_CD) info->IER |= UART_IER_MSI;
     if (cflag & CRTSCTS)
     {
          info->flags |= ASYNC_CTS_FLOW;
          info->IER   |= UART_IER_MSI;
     }
     else info->flags &= ~ASYNC_CTS_FLOW;
     if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD;
     else
     {
          info->flags |= ASYNC_CHECK_CD;
          info->IER   |= UART_IER_MSI;
     }
     serial_out(info, UART_IER, info->IER);

     // Set up parity check flag
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))

     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;
	
     // Characters to ignore
     info->ignore_status_mask = 0;
     if (I_IGNPAR(info->tty)) info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
     if (I_IGNBRK(info->tty))
     {
          info->ignore_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;
     }
     // !!! ignore all characters if CREAD is not set
     if ((cflag & CREAD) == 0) info->ignore_status_mask |= UART_LSR_DR;
     save_flags(flags);
     cli();
     if (uart_config[info->state->type].flags & UART_STARTECH)
     {
          serial_outp(info, UART_LCR, 0xBF);
          serial_outp(info, UART_EFR, (cflag & CRTSCTS) ? UART_EFR_CTS : 0);
     }
     serial_outp(info, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
     serial_outp(info, UART_DLL, quot & 0xff);	/* LS of divisor */
     serial_outp(info, UART_DLM, quot >> 8);		/* MS of divisor */
     if (info->state->type == PORT_16750) serial_outp(info, UART_FCR, fcr); 	/* set fcr */
     serial_outp(info, UART_LCR, cval);		/* reset DLAB */
     if (info->state->type != PORT_16750)
     {
          if (fcr & UART_FCR_ENABLE_FIFO)
          {
               /* emulated UARTs (Lucent Venus 167x) need two steps */
               serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
          }
          serial_outp(info, UART_FCR, fcr); 	/* set fcr */
     }
     restore_flags(flags);
}


static void rs_put_char(struct tty_struct *tty, unsigned char ch)
{
     struct async_struct *info = (struct async_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++;
     restore_flags(flags);
}


static void rs_flush_chars(struct tty_struct *tty)
{
     struct async_struct *info = (struct async_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;
     serial_out(info, UART_IER, info->IER);
     restore_flags(flags);
}


static int rs_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
{
     int c, ret = 0;
     struct async_struct *info = (struct async_struct *)tty->driver_data;
     unsigned long flags;
				
     if (!tty || !info->xmit_buf || !tmp_buf) return 0;

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

               c -= copy_from_user(tmp_buf, buf, c);
               if (!c)
               {
                    if (!ret) ret = -EFAULT;
                    break;
               }
               cli();
               c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head));
               memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
               info->xmit_head = ((info->xmit_head + c) & (SERIAL_XMIT_SIZE-1));
               info->xmit_cnt += c;
               restore_flags(flags);
               buf += c;
               count -= c;
               ret += c;
          }
          up(&tmp_buf_sem);
     }
     else
     {
          while (1)
          {
               cli();		
               c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head));
               if (c <= 0)
               {
                    restore_flags(flags);
                    break;
               }
               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;
               ret += c;
          }
     }
     if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && !(info->IER & UART_IER_THRI))
     {
          info->IER |= UART_IER_THRI;
          serial_out(info, UART_IER, info->IER);
     }
     return ret;
}


static int rs_write_room(struct tty_struct *tty)
{
     struct async_struct *info = (struct async_struct *)tty->driver_data;
     int                 ret;
				
     ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
     if (ret < 0) ret = 0;
     return ret;
}


static int rs_chars_in_buffer(struct tty_struct *tty)
{
     struct async_struct *info = (struct async_struct *)tty->driver_data;
				
     return info->xmit_cnt;
}


static void rs_flush_buffer(struct tty_struct *tty)
{
     struct async_struct *info = (struct async_struct *)tty->driver_data;
     unsigned long flags;
	
     save_flags(flags);
     cli();
     info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
     restore_flags(flags);
     wake_up_interruptible(&tty->write_wait);
     if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
          (tty->ldisc.write_wakeup)(tty);
}


// This function is used to send a high-priority XON/XOFF character to the device
static void rs_send_xchar(struct tty_struct *tty, char ch)
{
     struct async_struct *info = (struct async_struct *)tty->driver_data;

     info->x_char = ch;
     if (ch)
     {
          /* Make sure transmit interrupts are on */
          info->IER |= UART_IER_THRI;
          serial_out(info, UART_IER, info->IER);
     }
}


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

     if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty));
     if (tty->termios->c_cflag & CRTSCTS) info->MCR &= ~UART_MCR_RTS;
     save_flags(flags); cli();
     serial_out(info, UART_MCR, info->MCR);
     restore_flags(flags);
}


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

     if (I_IXOFF(tty))
     {
          if (info->x_char) info->x_char = 0;
          else rs_send_xchar(tty, START_CHAR(tty));
     }
     if (tty->termios->c_cflag & CRTSCTS) info->MCR |= UART_MCR_RTS;
     save_flags(flags); cli();
     serial_out(info, UART_MCR, info->MCR);
     restore_flags(flags);
}


// rs_ioctl() and friends
static int get_serial_info(struct async_struct * info, struct serial_struct * retinfo)
{
     struct serial_struct tmp;
     struct serial_state *state = info->state;
   
     if (!retinfo) return -EFAULT;
     memset(&tmp, 0, sizeof(tmp));
     tmp.type           = state->type;
     tmp.line           = state->line;
     tmp.port           = state->port;
     tmp.irq            = state->irq;
     tmp.flags          = state->flags;
     tmp.xmit_fifo_size = state->xmit_fifo_size;
     tmp.baud_base      = state->baud_base;
     tmp.close_delay    = state->close_delay;
     tmp.closing_wait   = state->closing_wait;
     tmp.custom_divisor = state->custom_divisor;
     tmp.hub6           = state->hub6;
     if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT;
     return 0;
}


static int set_serial_info(struct async_struct * info, struct serial_struct * new_info)
{
     struct serial_struct new_serial;
     struct serial_state  old_state, *state;
     unsigned int         i, change_irq,change_port;
     int                  retval = 0;

     if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT;
     state       = info->state;
     old_state   = *state;
     change_irq  = new_serial.irq != state->irq;
     change_port = (new_serial.port != state->port) || (new_serial.hub6 != state->hub6);
  
     if (!capable(CAP_SYS_ADMIN))
     {
          if (change_irq || change_port                      ||
              (new_serial.baud_base != state->baud_base)     ||
              (new_serial.type != state->type)               ||
              (new_serial.close_delay != state->close_delay) ||
              (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
              ((new_serial.flags & ~ASYNC_USR_MASK) != (state->flags & ~ASYNC_USR_MASK)))
                   return -EPERM;
          state->flags          = ((state->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK));
          info->flags           = ((info->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK));
          state->custom_divisor = new_serial.custom_divisor;
          goto check_and_exit;
     }

     new_serial.irq = irq_cannonicalize(new_serial.irq);

     if ((new_serial.irq >= NR_IRQS)  || (new_serial.port > 0xffff)       ||
         (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) ||
         (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) ||
         (new_serial.type == PORT_STARTECH))
     {
          return -EINVAL;
     }

     if ((new_serial.type != state->type) || (new_serial.xmit_fifo_size <= 0))
          new_serial.xmit_fifo_size = uart_config[new_serial.type].dfl_xmit_fifo_size;

     /* Make sure address is not already in use */
     if (new_serial.type)
     {
          for (i = 0 ; i < NR_PORTS; i++)
               if ((state != &rs_table[i]) && (rs_table[i].port == new_serial.port) && rs_table[i].type)
                    return -EADDRINUSE;
     }

     if ((change_port || change_irq) && (state->count > 1)) return -EBUSY;

     // OK, past this point, all the error checking has been done. At this point, we start making changes.....
     state->baud_base       = new_serial.baud_base;
     state->flags           = ((state->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS));
     info->flags            = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | (info->flags & ASYNC_INTERNAL_FLAGS));
     state->custom_divisor  = new_serial.custom_divisor;
     state->type            = new_serial.type;
     state->close_delay     = new_serial.close_delay * HZ/100;
     state->closing_wait    = new_serial.closing_wait * HZ/100;
     info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
     info->xmit_fifo_size   = state->xmit_fifo_size = new_serial.xmit_fifo_size;

     if (IBMASMwisemanexists) release_region(state->port, 8);
     if (change_port || change_irq)
     {
          // We need to shutdown the serial port at the old port/irq combination.
          shutdown(info);
          state->irq = new_serial.irq;
          info->port = state->port = new_serial.port;
          info->hub6 = state->hub6 = new_serial.hub6;
     }
     if (IBMASMwisemanexists && (state->type != PORT_UNKNOWN)) request_region(state->port, 8, "serial(set)");

check_and_exit:
     if (!state->port || !state->type) return 0;
     if (info->flags & ASYNC_INITIALIZED)
     {
          if (((old_state.flags & ASYNC_SPD_MASK) != (state->flags & ASYNC_SPD_MASK)) || (old_state.custom_divisor != state->custom_divisor))
          {
               if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)   info->tty->alt_speed = 57600;
               if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)  info->tty->alt_speed = 115200;
               if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)  info->tty->alt_speed = 230400;
               if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800;
               change_speed(info, 0);
          }
     }
     else retval = startup(info);
     return retval;
}


// ***********************************************************************
// * 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 get_lsr_info(struct async_struct * info, unsigned int *value)
{
     unsigned char status;
     unsigned int  result;
     unsigned long flags;

     save_flags(flags);
     cli();
     status = serial_in(info, UART_LSR);
     restore_flags(flags);
     result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
     return put_user(result,value);
}


static int get_modem_info(struct async_struct * info, unsigned int *value)
{
     unsigned char control, status;
     unsigned int result;
     unsigned long flags;

     control = info->MCR;
     save_flags(flags);
     cli();
     status = serial_in(info, UART_MSR);
     restore_flags(flags);
     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);
     return put_user(result,value);
}


static int set_modem_info(struct async_struct * info, unsigned int cmd, unsigned int *value)
{
     int           error;
     unsigned int  arg;
     unsigned long flags;

     error = get_user(arg, value);
     if (error) return error;
     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;
     }
     save_flags(flags);
     cli();
     serial_out(info, UART_MCR, info->MCR);
     restore_flags(flags);
     return 0;
}


static int do_autoconfig(struct async_struct * info)
{
     int retval;
	
     if (!capable(CAP_SYS_ADMIN)) return -EPERM;
     if (info->state->count > 1)  return -EBUSY;
	
     shutdown(info);

     autoconfig(info->state);
     if ((info->state->flags & ASYNC_AUTO_IRQ) && (info->state->port != 0) && (info->state->type != PORT_UNKNOWN)) info->state->irq = IBMirq;   // MMDIRQ

     retval = startup(info);
     if (retval) return retval;
     return 0;
}


// rs_break() --- routine which turns the break handling on or off
static void rs_break(struct tty_struct *tty, int break_state)
{
     struct async_struct * info = (struct async_struct *)tty->driver_data;
     unsigned long flags;
	
     if (!info->port) return;
     save_flags(flags);
     cli();
     if (break_state == -1) serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
     else                   serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
     restore_flags(flags);
}


static int rs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
{
     int error;
     struct async_struct * info = (struct async_struct *)tty->driver_data;
     struct async_icount cprev, cnow;	/* kernel counter temps */
     struct serial_icounter_struct *p_cuser;	/* user space */
     unsigned long flags;
	
     if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT))
     { 
          if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO;
     }
	
     switch (cmd)
     {
          case TIOCMGET:       return get_modem_info(info, (unsigned int *) arg);
          case TIOCMBIS:
          case TIOCMBIC:
          case TIOCMSET:       return set_modem_info(info, cmd, (unsigned int *) arg);
          case TIOCGSERIAL:    return get_serial_info(info, (struct serial_struct *) arg);
          case TIOCSSERIAL:    return set_serial_info(info, (struct serial_struct *) arg);
          case TIOCSERCONFIG:  return do_autoconfig(info);
          case TIOCSERGETLSR:  /* Get line status register */
                               return get_lsr_info(info, (unsigned int *) arg);
          case TIOCSERGSTRUCT: if (copy_to_user((struct async_struct *) arg, info, sizeof(struct async_struct))) return -EFAULT;
                               return 0;
				
          // 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: save_flags(flags);
                           cli();
                           /* note the counters on entry */
                           cprev = info->state->icount;
                           restore_flags(flags);
                           while (1)
                           {
                                interruptible_sleep_on(&info->delta_msr_wait);
                                /* see if a signal did it */
                                if (signal_pending(current)) return -ERESTARTSYS;
                                save_flags(flags); cli();
                                cnow = info->state->icount; /* atomic copy */
                                restore_flags(flags);
                                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: save_flags(flags);
                            cli();
                            cnow = info->state->icount;
                            restore_flags(flags);
                            p_cuser = (struct serial_icounter_struct *) arg;
                            error = put_user(cnow.cts, &p_cuser->cts);
                            if (error) return error;
                            error = put_user(cnow.dsr, &p_cuser->dsr);
                            if (error) return error;
                            error = put_user(cnow.rng, &p_cuser->rng);
                            if (error) return error;
                            error = put_user(cnow.dcd, &p_cuser->dcd);
                            if (error) return error;
                            error = put_user(cnow.rx, &p_cuser->rx);
                            if (error) return error;
                            error = put_user(cnow.tx, &p_cuser->tx);
                            if (error) return error;
                            error = put_user(cnow.frame, &p_cuser->frame);
                            if (error) return error;
                            error = put_user(cnow.overrun, &p_cuser->overrun);
                            if (error) return error;
                            error = put_user(cnow.parity, &p_cuser->parity);
                            if (error) return error;
                            error = put_user(cnow.brk, &p_cuser->brk);
                            if (error) return error;
                            error = put_user(cnow.buf_overrun, &p_cuser->buf_overrun);
                            if (error) return error;			
                            return 0;
          case TIOCSERGWILD:
          case TIOCSERSWILD: /* "setserial -W" is called in Debian boot */
                             printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
                             return 0;
          default:           return -ENOIOCTLCMD;
     }
     return 0;
}


static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
     struct async_struct *info = (struct async_struct *)tty->driver_data;
     unsigned long flags;
     unsigned int cflag = tty->termios->c_cflag;
	
     if ((cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) return;

     change_speed(info, old_termios);

     /* Handle transition to B0 status */
     if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
     {
          info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
          save_flags(flags);
          cli();
          serial_out(info, UART_MCR, info->MCR);
          restore_flags(flags);
     }
	
     /* Handle transition away from B0 status */
     if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD))
     {
          info->MCR |= UART_MCR_DTR;
          if (!(tty->termios->c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags))
          {
               info->MCR |= UART_MCR_RTS;
          }
          save_flags(flags);
          cli();
          serial_out(info, UART_MCR, info->MCR);
          restore_flags(flags);
     }
	
     /* Handle turning off CRTSCTS */
     if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS))
     {
          tty->hw_stopped = 0;
          rs_start(tty);
     }
}


// ************************************************************************************
// * rs_close() - 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 rs_close(struct tty_struct *tty, struct file * filp)
{
     struct async_struct * info = (struct async_struct *)tty->driver_data;
     struct serial_state *state;
     unsigned long flags;

     // RJ01 Begin
     if (Special_Flag == 1) 
     {
       //Set Special Flag to fasle and cancel the timer
       Special_Flag = 0;
       del_timer(&UARTAccessTimer);

     }
     //RJ01 End

     if (!info) return;
     state = info->state;
	
     save_flags(flags);
     cli();
	
     if (tty_hung_up_p(filp))
     {
          MOD_DEC_USE_COUNT;
          restore_flags(flags);
          return;
     }
	
     if ((tty->count == 1) && (state->count != 1))
     {
          // Uh, oh.  tty->count is 1, which means that the tty structure will be freed.  state->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("rs_close: bad serial port count; tty->count is 1,\nstate->count is %d\n", state->count);
          state->count = 1;
     }
     if (--state->count < 0)
     {
          printk("rs_close: bad serial port count for ttys%d: %d\n", info->line, state->count);
          state->count = 0;
     }
     if (state->count)
     {
          MOD_DEC_USE_COUNT;
          restore_flags(flags);
          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->state->normal_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;
     info->read_status_mask &= ~UART_LSR_DR;
     if (info->flags & ASYNC_INITIALIZED)
     {
          serial_out(info, UART_IER, info->IER);
          // Before we drop DTR, make sure the UART transmitter has completely drained; this is especially important if there is a transmit FIFO!
          rs_wait_until_sent(tty, info->timeout);
     }
     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);
     MOD_DEC_USE_COUNT;
     restore_flags(flags);
}


// rs_wait_until_sent() --- wait until the transmitter is empty
static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
{
     struct async_struct * info = (struct async_struct *)tty->driver_data;
     unsigned long orig_jiffies, char_time;
     int lsr;
	
     if (info->state->type == PORT_UNKNOWN) return;
     if (info->xmit_fifo_size == 0) return; /* Just in case.... */

     orig_jiffies = jiffies;

     // Set the check interval to be 1/5 of the estimated time to send a single character, and make it at least 1.  The check
     // interval should also be less than the timeout.
     // Note: we have to use pretty tight timings here to satisfy the NIST-PCTS.
     char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
     char_time = char_time / 5;
     if (char_time == 0) char_time = 1;
     if (timeout) char_time = MIN(char_time, timeout);

     // If the transmitter hasn't cleared in twice the approximate amount of time to send the entire FIFO, it probably won't
     // ever clear.  This assumes the UART isn't doing flow control, which is currently the case.  Hence, if it ever
     // takes longer than info->timeout, this is probably due to a UART bug of some kind.  So, we clamp the timeout parameter at
     // 2*info->timeout.
     if (!timeout || timeout > 2*info->timeout) timeout = 2*info->timeout;
     while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT))
     {
          current->state = TASK_INTERRUPTIBLE;
          current->counter = 0;	/* make us low-priority */
          schedule_timeout(char_time);
          if (signal_pending(current)) break;
          if (timeout && time_after(jiffies, orig_jiffies + timeout)) break;
     }
     current->state = TASK_RUNNING;
}


// rs_hangup() --- called by tty_hangup() when a hangup is signaled.
static void rs_hangup(struct tty_struct *tty)
{
     struct async_struct * info = (struct async_struct *)tty->driver_data;
     struct serial_state *state = info->state;
	
     state        = info->state;
     rs_flush_buffer(tty);
     shutdown(info);
     info->event  = 0;
     state->count = 0;
     info->flags  &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
     info->tty    = 0;
     wake_up_interruptible(&info->open_wait);
}


// rs_open() and friends
static int block_til_ready(struct tty_struct *tty, struct file * filp, struct async_struct *info)
{
     struct  wait_queue wait = { current, NULL };
     struct  serial_state *state = info->state;
     int     retval;
     int     do_clocal = 0, extra_count = 0;
     unsigned long flags;

     // 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);
          return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);
     }

     // 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 (state->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, state->count is dropped by one, so that rs_close() knows when to free things.  We restore it upon
     // exit, either normal or abnormal.
     retval = 0;
     add_wait_queue(&info->open_wait, &wait);
     save_flags(flags);
     cli();
     if (!tty_hung_up_p(filp))
     {
          extra_count = 1;
          state->count--;
     }
     restore_flags(flags);
     info->blocked_open++;
     while (1)
     {
          save_flags(flags);
          cli();
          if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && (tty->termios->c_cflag & CBAUD)) serial_out(info, UART_MCR, serial_inp(info, UART_MCR) | (UART_MCR_DTR | UART_MCR_RTS));
          restore_flags(flags);
          current->state = TASK_INTERRUPTIBLE;
          if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED))
          {
               if (info->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN;
               else retval = -ERESTARTSYS;	
               break;
          }
          if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && !(info->flags & ASYNC_CLOSING) && (do_clocal || (serial_in(info, 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 (extra_count) state->count++;
     info->blocked_open--;
     if (retval) return retval;
     info->flags |= ASYNC_NORMAL_ACTIVE;
     return 0;
}


// **************************************************************
// * get_async_struct() - Finishes initializing the info struct *
// **************************************************************

static int get_async_struct(int line, struct async_struct **ret_info)
{
     struct async_struct *info;
     struct serial_state *sstate;

     sstate = rs_table + line;
     sstate->count++;
     if (sstate->info)
     {
          *ret_info = sstate->info;
          return 0;
     }
     info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
     if (!info)
     {
          sstate->count--;
          return -ENOMEM;
     }
     memset(info, 0, sizeof(struct async_struct));
     info->magic          = SERIAL_MAGIC;
     info->port           = sstate->port;
     info->flags          = sstate->flags;
     info->xmit_fifo_size = sstate->xmit_fifo_size;
     info->line           = line;
     info->tqueue.routine = do_softint;
     info->tqueue.data    = info;
     info->state          = sstate;
     if (sstate->info)
     {
          kfree_s(info, sizeof(struct async_struct));
          *ret_info = sstate->info;
          return 0;
     }
     *ret_info = sstate->info = info;
     return 0;
}


// **************************************************************************************************
// * rs_open() - Called whenever this serial port is opened by an application. It registers the IRQ *
// *             handler, enables COM interrupts on the ASM, and performs remaining initialization. *
// **************************************************************************************************

static int rs_open(struct tty_struct *tty, struct file * filp)
{
     struct async_struct *info;
     int                 retval, line;
     unsigned long       page;

     /*RJ01 Begin */
     unsigned char host_flag;

     host_flag = IBM_QueryHostFlag();
     if ( host_flag == 0)
     {
         /* Host flag indicates that serial port is unavailable for use by host. */
         /* Return not available status to application */
         //printk(KERN_CRIT "rs_open : Setting Special_Flag to 1.\n"); //RJ01
         Special_Flag = 1 ;
         //return -ENODEV;
     }
     else
     {
         /* Host flag indicates that serial port can be used by host. */
         /* Continue processing normally */

         Special_Flag = 0 ;
         //printk(KERN_CRIT "rs_open : Setting Special_Flag to 0.\n"); //RJ01
          //Enable COM PCI interrupt
          IBM_EnableInterrupts();
     }
     /*RJ01 End */


     // DETERMINE LINE DISCIPLINE
     MOD_INC_USE_COUNT;
     line = MINOR(tty->device) - tty->driver.minor_start;
     if ((line < 0) || (line >= NR_PORTS))
     { 
          MOD_DEC_USE_COUNT;
          return -ENODEV;
     }

     // FINISH info STRUCT INITIALIZATION
     if ((retval = get_async_struct(line, &info)))
     {
          MOD_DEC_USE_COUNT;
          return retval;
     }

     // LINK THE info STRUCT AND TTY DRIVER STRUCT TO EACH OTHER
     tty->driver_data = info;
     info->tty = tty;

     // MORE INITIALIZATION
     info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;

     // INITIALIZE tmp_buf (GLOBAL STRUCT)
     if (!tmp_buf)
     {
          page = get_free_page(GFP_KERNEL);
          if (!page) return -ENOMEM;
          if (tmp_buf) free_page(page);
          else tmp_buf = (unsigned char *)page;
     }

     // IF THE PORT IS ALREADY CLOSING, ABORT
     if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING))
     {
          if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait);
          return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);
     }

     // START UP SERIAL PORT
     if ((retval = startup(info))) return retval;

     // BLOCK UNTIL READY
     if ((retval = block_til_ready(tty, filp, info))) return retval;

     // REMAINING INITIALIZATION
     if ((info->state->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS))
     {
          *tty->termios = info->state->normal_termios;
          change_speed(info, 0);
     }
     info->session = current->session;
     info->pgrp = current->pgrp;
     return 0;
}


// ****************************
// * PROC FILESYSTEM ROUTINES *
// ****************************

static inline int line_info(char *buf, struct serial_state *state)
{
     struct async_struct *info = state->info, scr_info;
     char	stat_buf[30], control, status;
     int	ret;
     unsigned long flags;

     ret = sprintf(buf, "%d: uart:%s port:%X irq:%d", state->line, uart_config[state->type].name, state->port, state->irq);

     if (!state->port || (state->type == PORT_UNKNOWN))
     {
          ret += sprintf(buf+ret, "\n");
          return ret;
     }

     // Figure out the current RS-232 lines
     if (!info)
     {
          info = &scr_info;	/* This is just for serial_{in,out} */
          info->magic = SERIAL_MAGIC;
          info->port = state->port;
          info->flags = state->flags;
          info->quot = 0;
          info->tty = 0;
     }
     save_flags(flags);
     cli();
     status = serial_in(info, UART_MSR);
     control = info ? info->MCR : serial_in(info, UART_MCR);
     restore_flags(flags); 
	
     stat_buf[0] = 0;
     stat_buf[1] = 0;
     if (control & UART_MCR_RTS) strcat(stat_buf, "|RTS");
     if (status  & UART_MSR_CTS) strcat(stat_buf, "|CTS");
     if (control & UART_MCR_DTR) strcat(stat_buf, "|DTR");
     if (status  & UART_MSR_DSR) strcat(stat_buf, "|DSR");
     if (status  & UART_MSR_DCD) strcat(stat_buf, "|CD");
     if (status  & UART_MSR_RI)  strcat(stat_buf, "|RI");
     if (info->quot) ret += sprintf(buf+ret, " baud:%d", state->baud_base / info->quot);

     ret += sprintf(buf+ret, " tx:%d rx:%d", state->icount.tx, state->icount.rx);
     if (state->icount.frame)   ret += sprintf(buf+ret, " fe:%d",  state->icount.frame);
     if (state->icount.parity)  ret += sprintf(buf+ret, " pe:%d",  state->icount.parity);
     if (state->icount.brk)     ret += sprintf(buf+ret, " brk:%d", state->icount.brk);	
     if (state->icount.overrun) ret += sprintf(buf+ret, " oe:%d",  state->icount.overrun);

     // Last thing is the RS-232 status lines
     ret += sprintf(buf+ret, " %s\n", stat_buf+1);
     return ret;
}


int rs_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
     int i, len = 0, l;
     off_t begin = 0;

     len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
     for (i = 0; i < NR_PORTS && len < 3900; i++)
     {
          l = line_info(page + len, &rs_table[i]);
          len += l;
          if (len+begin > off+count) goto done;
          if (len+begin < off)
          {
               begin += len;
               len = 0;
          }
     }
     *eof = 1;
done:
     if (off >= len+begin) return 0;
     *start = page + (begin-off);
     return ((count < begin+len-off) ? count : begin+len-off);
}


// ********************************************************************************************************
// * autoconfig() - Called by rs_init() to initialize a specific serial port. It determines the UART chip *
// *                (8250, 16450, 16550, 16550A). If a 16550A, we can use its special FIFO features.      *
// ********************************************************************************************************

static void autoconfig(struct serial_state *state)
{
     unsigned char       status1, status2, scratch, scratch2;
     struct async_struct *info, scr_info;
     unsigned long       flags;

     // INITIALIZE TYPE
     state->type = PORT_UNKNOWN;
	
     // ENSURE WISEMAN HAS A BASE ADDRESS SPECIFIED
     if (IBMASMwisemanexists && !state->port) return;
		
     // BEGIN info STRUCT INITIALIZATION
     info = &scr_info;
     info->magic = SERIAL_MAGIC;
     info->port  = state->port;
     info->flags = state->flags;

     // TEST FOR ISA BUS FLOAT - ASSUMING 0x80 IS A NON-EXISTENT PORT, TEST READ/WRITE
     save_flags(flags);
     cli();
     scratch = serial_inp(info, UART_IER);
     serial_outp(info, UART_IER, 0);
     outb(0xff, 0x080);
     scratch2 = serial_inp(info, UART_IER);
     serial_outp(info, UART_IER, scratch);
     if (scratch2)
     {
          restore_flags(flags);
          return;
     }

     // TEST FOR ACTUAL UART WITH LOOPBACK TEST MODE
     if (!(state->flags & ASYNC_SKIP_TEST))
     {
          scratch = serial_inp(info, UART_MCR);
          serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
          serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
          status1 = serial_inp(info, UART_MSR) & 0xF0;
          serial_outp(info, UART_MCR, scratch);
          if (status1 != 0x90)
          {
               restore_flags(flags);
               return;
          }
     } 
	
     // DO BASIC UART TYPE DETECTION
     scratch2 = serial_in(info, UART_LCR);
     serial_outp(info, UART_LCR, 0xBF);  // set up for StarTech test
     serial_outp(info, UART_EFR, 0);     // EFR is the same as FCR
     serial_outp(info, UART_LCR, 0);
     serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
     scratch = serial_in(info, UART_IIR) >> 6;
     switch (scratch)
     {
          case 0: state->type = PORT_16450;
                  break;
          case 1: state->type = PORT_UNKNOWN;
                  break;
          case 2: state->type = PORT_16550;
                  break;
          case 3: state->type = PORT_16550A;
                  break;
     }

     // DO FURTHER UART TYPE DETECTION - CHECK FOR STARTECH UART
     if (state->type == PORT_16550A)
     {
          serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
          if (serial_in(info, UART_EFR) == 0) state->type = PORT_16650;
          else
          {
               serial_outp(info, UART_LCR, 0xBF);
               if (serial_in(info, UART_EFR) == 0) state->type = PORT_16650V2;
          }
     }

     // DO FURTHER UART TYPE DETECTION - CHECK FOR TI 16750
     if (state->type == PORT_16550A)
     {
          serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
          serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
          scratch = serial_in(info, UART_IIR) >> 5;
          if (scratch == 7)
          {
               serial_outp(info, UART_LCR, 0);
               scratch = serial_in(info, UART_IIR) >> 5;
               if (scratch == 6) state->type = PORT_16750;
          }
          serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
     }

     // DO FURTHER UART TYPE DETECTION
     serial_outp(info, UART_LCR, scratch2);
     if (state->type == PORT_16450)
     {
          scratch = serial_in(info, UART_SCR);
          serial_outp(info, UART_SCR, 0xa5);
          status1 = serial_in(info, UART_SCR);
          serial_outp(info, UART_SCR, 0x5a);
          status2 = serial_in(info, UART_SCR);
          serial_outp(info, UART_SCR, scratch);
          if ((status1 != 0xa5) || (status2 != 0x5a)) state->type = PORT_8250;
     }
     state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size;

     // IF STILL UNABLE TO DETECT UART TYPE, ABORT
     if (state->type == PORT_UNKNOWN)
     {
          restore_flags(flags);
          return;
     }

     // IF WISEMAN, EXECUTE request_region ON ITS BASE ADDRESS
     if (IBMASMwisemanexists) request_region(info->port, 8, "serial(auto)");

     // RESET UART
     serial_outp(info, UART_MCR, 0x00);
     serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
     (void)serial_in(info, UART_RX);
     serial_outp(info, UART_IER, 0);
     restore_flags(flags);
}


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

__initfunc(int rs_init(void))
{
     int i;
     struct serial_state * state;

     init_bh(SERIAL_BH, do_serial_bh);

     for (i = 0; i < NR_IRQS; i++)
     {
          IRQ_ports[i]   = 0;
          IRQ_timeout[i] = 0;
     }

     // INITIALIZE TTY DRIVER STRUCT WITH POINTERS AND DATA
     memset(&serial_driver, 0, sizeof(struct tty_driver));
     serial_driver.magic                = TTY_DRIVER_MAGIC;
     serial_driver.driver_name          = "ibmser";
     serial_driver.name                 = "ibmser";
     serial_driver.major                = 253;
     serial_driver.minor_start          = 0;
     serial_driver.num                  = NR_PORTS;
     serial_driver.type                 = TTY_DRIVER_TYPE_SERIAL;
     serial_driver.subtype              = SERIAL_TYPE_NORMAL;
     serial_driver.init_termios         = tty_std_termios;
     serial_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
     serial_driver.flags                = TTY_DRIVER_REAL_RAW;
     serial_driver.refcount             = &serial_refcount;
     serial_driver.table                = serial_table;
     serial_driver.termios              = serial_termios;
     serial_driver.termios_locked       = serial_termios_locked;

     // INITIALIZE TTY DRIVER STRUCT WITH ENTRY POINTS
     serial_driver.open            = rs_open;
     serial_driver.close           = rs_close;
     serial_driver.write           = rs_write;
     serial_driver.put_char        = rs_put_char;
     serial_driver.flush_chars     = rs_flush_chars;
     serial_driver.write_room      = rs_write_room;
     serial_driver.chars_in_buffer = rs_chars_in_buffer;
     serial_driver.flush_buffer    = rs_flush_buffer;
     serial_driver.ioctl           = rs_ioctl;
     serial_driver.throttle        = rs_throttle;
     serial_driver.unthrottle      = rs_unthrottle;
     serial_driver.send_xchar      = rs_send_xchar;
     serial_driver.set_termios     = rs_set_termios;
     serial_driver.stop            = rs_stop;
     serial_driver.start           = rs_start;
     serial_driver.hangup          = rs_hangup;
     serial_driver.break_ctl       = rs_break;
     serial_driver.wait_until_sent = rs_wait_until_sent;
     serial_driver.read_proc       = rs_read_proc;
	
     // REGISTER TTY DRIVER STRUCT
     if (tty_register_driver(&serial_driver)) panic("Couldn't register serial driver\n");
	
     // INITIALIZE THE STATE STRUCT
     for (i = 0, state = rs_table; i < NR_PORTS; i++,state++)
     {
          state->magic          = SSTATE_MAGIC;
          state->line           = i;
          state->type           = PORT_UNKNOWN;
          state->custom_divisor = 0;
          state->close_delay    = 5*HZ/10;
          state->closing_wait   = 30*HZ;
          state->normal_termios = serial_driver.init_termios;
          state->icount.cts     = state->icount.dsr = 
          state->icount.rng     = state->icount.dcd = 0;
          state->icount.rx      = state->icount.tx = 0;
          state->icount.frame   = state->icount.parity = 0;
          state->icount.overrun = state->icount.brk = 0;
          state->irq            = IBMirq;
          if (IBMASMwisemanexists)
          {
               state->port = IBMport;
               if (check_region(state->port,8)) continue;
          }
          else state->port = 1;
          if (state->flags & ASYNC_BOOT_AUTOCONF) autoconfig(state);
     }

     // DISPLAY BANNER
#ifdef __SMP__
     printk(KERN_CRIT "IBM Advanced System Management SMP Shared Serial Driver loaded.\n");
#else
     printk(KERN_CRIT "IBM Advanced System Management Shared Serial Driver loaded.\n");
#endif
     return 0;
}


int init_module(void)
{

    unsigned long wait = jiffies + 4 * HZ;

     // ENSURE ibmasm DRIVER IS ALREADY LOADED
     if (!IBMASMibmasmexists)
     {
          return -ENODEV;
     }

     // IF THIS IS NEITHER WISEMAN OR EAGLE, GO COMATOSE
     if (!IBMASMwisemanexists && !IBMASMeagleexists) return 0;

     // GET WISEMAN/EAGLE DEVICE INFORMATION (UPDATES IBMirq/IBMport)
     IBM_GetDeviceInformation();

     //The SP driver has just loaded, Sometimes there is a delay in 
     //the host flag to be updated. Wait for a max. of 4 seconds.
     while (jiffies < wait) 
     {
        Host_Flag_On_Startup = IBM_QueryHostFlag(); //RJ01
        if (Host_Flag_On_Startup != 0) 
        {
           break;
        }
        schedule();
     }

     // ENSURE COM INTERRUPTS ARE DISABLED, SINCE WE HAVEN'T REGISTERED IRQ
     IBM_DisableInterrupts();
     init_timer(&(UARTAccessTimer)); //RJ01
     return rs_init();
}


void cleanup_module(void) 
{
     unsigned long flags;
     int i;
     struct async_struct *info;

     // IF THIS IS NEITHER WISEMAN OR EAGLE, SIMPLY RETURN
     if (!IBMASMwisemanexists && !IBMASMeagleexists) return;

     // DISABLE SHARED SERIAL INTERRUPTS
     IBM_DisableInterrupts();

     // RELEASE THE INTERRUPT
     free_irq(IBMirq, (void*)&IBM_devid);

     save_flags(flags);
     cli();
     remove_bh(SERIAL_BH);
     if ((i = tty_unregister_driver(&serial_driver))) printk("SERIAL: failed to unregister serial driver (%d)\n", i);
     restore_flags(flags);

     for (i = 0; i < NR_PORTS; i++)
     {
          if (IBMASMwisemanexists && (rs_table[i].type != PORT_UNKNOWN)) release_region(rs_table[i].port, 8);
          info = rs_table[i].info;
          if (info)
          {
               rs_table[i].info = NULL;
               kfree_s(info, sizeof(struct async_struct));
          }
     }
     if (tmp_buf)
     {
          free_page((unsigned long) tmp_buf);
          tmp_buf = NULL;
     }
     printk(KERN_CRIT "IBM Advanced System Management Shared Serial Driver unloaded.\n");
}


