/*
 * Copyright (c) 2006 Jon Sevy <jsevy@cs.drexel.edu>
 * All rights reserved.
 * 
 * Based on ixp12x0_com.c
 * 
 * Copyright (c) 1998, 1999, 2001, 2002 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Ichiro FUKUHARA and Naoto Shimazaki.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by IWAMOTO Toshihiro.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Charles M. Hannum.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *        This product includes software developed by the NetBSD
 *        Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1991 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      @(#)com.c       7.5 (Berkeley) 5/16/91
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vx115_com.c,v 1.19 2003/08/07 16:26:53 agc Exp $");

#include "opt_ddb.h"
#include "opt_kgdb.h"

#include "rnd.h"
#if NRND > 0 && defined(RND_COM)
#include <sys/rnd.h>
#endif

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <sys/uio.h>
#include <sys/vnode.h>

#include <machine/intr.h>
#include <machine/bus.h>

#include <arm/vx115/vx115_reg.h>
#include <arm/vx115/vx115_com.h>
#include <arm/vx115/vx115_com_var.h>
#include <arm/vx115/vx115_var.h>

#include <dev/cons.h>


#define DEBUG_COM
//#define LOWLEVEL_DEBUG_COM

#ifdef DEBUG_COM
#define DPRINTF(fmt...)  printf(fmt)
void COM_DEBUG_PRINT_STRING(char *str);
#ifdef LOWLEVEL_DEBUG_COM
#define COM_LOWLEVEL_DEBUG_PRINT_STRING(str)  COM_DEBUG_PRINT_STRING(str)
#else
#define COM_LOWLEVEL_DEBUG_PRINT_STRING(str) 
#endif
#else
#define DPRINTF(fmt...) 
#define COM_DEBUG_PRINT_STRING(str) 
#define COM_LOWLEVEL_DEBUG_PRINT_STRING(str)
#endif




/* Vx115 com softc struct */
struct vx115_com_softc 
{
    struct device       sc_dev;
    bus_addr_t          sc_addr;
    bus_space_tag_t     sc_iot;
    bus_space_handle_t  sc_ioh;

    void    *sc_softintr;

    struct tty *sc_tty;

    u_char  *sc_rbuf, *sc_ebuf;

    u_char  *sc_tba;
    u_int   sc_tbc, sc_heldtbc;

    u_char  *sc_rbget,*sc_rbput;
    u_int sc_rbavail;

    /* status flags */
    u_int sc_hwflags, sc_swflags;

    u_int sc_rx_flags;
    int sc_tx_busy;
    int sc_tx_stopped;
    int sc_rx_ready;
    int sc_heldchange;

    /* control registers */
    u_int sc_baud_divisor;
    u_int sc_rx_control;
    u_int sc_tx_control;
    u_int sc_char_counter_control;
    
    /* power management hooks */
    int (*enable)(struct vx115_com_softc *);
    int (*disable)(struct vx115_com_softc *);

    int enabled;
};


/* Vx115 com console softc struct */
struct vx115_com_cons_softc 
{
    bus_space_tag_t        sc_iot;
    bus_space_handle_t     sc_ioh;
    bus_addr_t             sc_addr;
    int                    sc_ospeed;
    tcflag_t               sc_cflag;
    int                    sc_attached;
};


/* Utility functions */
static void vx115_com_break(struct vx115_com_softc *sc, int onoff);
static void vx115_com_shutdown(struct vx115_com_softc *sc);
static void vx115_com_iflush(struct vx115_com_softc *);
static void vx115_com_set_cr(struct vx115_com_softc *);
static u_int cflag_to_rx_control(tcflag_t cflag);
static u_int cflag_to_tx_control(tcflag_t cflag);

/* tty functions */
static int vx115_com_param(struct tty *, struct termios *);
static int vx115_com_hwiflow(struct tty *, int);
static void vx115_com_start(struct tty *);

/* Console functions */
static int vx115_com_cngetc(dev_t);
static void vx115_com_cnputc(dev_t, int);
static void vx115_com_cnpollc(dev_t, int);

/* Soft interrupt functions */
static void vx115_com_soft(void* arg);
inline static void vx115_com_tx_soft(struct vx115_com_softc *, struct tty *);
inline static void vx115_com_rx_soft(struct vx115_com_softc *, struct tty *);

/* Hardware interrupt functions */
static void vx115_com_tx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh);
static void vx115_com_rx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh);




static struct vx115_com_cons_softc vx115_com_cn_sc;

static struct cnm_state vx115_com_cnm_state;

extern struct cfdriver vx115_com_cd;


dev_type_open(vx115_com_open);
dev_type_close(vx115_com_close);
dev_type_read(vx115_com_read);
dev_type_write(vx115_com_write);
dev_type_ioctl(vx115_com_ioctl);
dev_type_stop(vx115_com_stop);
dev_type_tty(vx115_com_tty);
dev_type_poll(vx115_com_poll);

/* char dev switch structure for tty */
const struct cdevsw vx115_com_cdevsw = 
{
    vx115_com_open,
    vx115_com_close, 
    vx115_com_read, 
    vx115_com_write, 
    vx115_com_ioctl,
    vx115_com_stop, 
    vx115_com_tty, 
    vx115_com_poll, 
    nommap,             /* mmap not supported */
    ttykqfilter, 
    D_TTY
};

/* console method struct */
struct consdev vx115_com_cons = 
{
    NULL,               /* cn_probe: probe hardware and fill in consdev info */
    NULL,               /* cn_init: turn on as console */
    vx115_com_cngetc,   /* cn_getc: kernel getchar interface */
    vx115_com_cnputc,   /* cn_putc: kernel putchar interface */
    vx115_com_cnpollc,  /* cn_pollc: turn on and off polling */
    NULL,               /* cn_bell: ring bell */
    NULL,               /* cn_halt: stop device */
    NULL,               /* cn_flush: flush output */
    NODEV,              /* major/minor of device */
    CN_NORMAL           /* priority */
};



#ifndef DEFAULT_COMSPEED
#define DEFAULT_COMSPEED 115200
#endif

#define COMUNIT_MASK    0x7ffff
#define COMDIALOUT_MASK 0x80000
#define COMUNIT(x)      (minor(x) & COMUNIT_MASK)
#define COMDIALOUT(x)   (minor(x) & COMDIALOUT_MASK)

#define COM_ISALIVE(sc)     ((sc)->enabled != 0 && ISSET((sc)->sc_dev.dv_flags, DVF_ACTIVE))

#define COM_BARRIER(t,h,f)  bus_space_barrier((t), (h), 0, COM_NPORTS, (f))

/* we're uniprocessor, so no need for locks */
#define COM_LOCK(sc)    
#define COM_UNLOCK(sc)

/* macros to make code more readable */
#define REG_READ(offset)          bus_space_read_4(iot, ioh, offset)
#define REG_WRITE(offset,value)   bus_space_write_4(iot, ioh, offset, value)

#define SET(t,f)    (t) |= (f)
#define CLR(t,f)    (t) &= ~(f)
#define ISSET(t,f)  ((t) & (f))


/* receive interrupts: FIFO threshold */
#define RECEIVE_INTERRUPT_MASK        ASCC_RX_CTRL_FICE_TH

/* character counter interrupt mask */
#define CHAR_CNT_CNTL_INTERRUPT_MASK  (ASCC_CHAR_COUNTER_CONTROL_ENABLE_FREERUNNING | ASCC_CHAR_COUNTER_CONTROL_INT_ENABLE)

/* transmit interrupts: FIFO threshold */
#define TRANSMIT_INTERRUPT_MASK       ASCC_TX_CTRL_FICE_TH


#ifdef DEBUG_COM
void COM_DEBUG_PRINT_STRING(char *str)
{
    while(*str != '\0')
    {
        bus_space_tag_t     iot = vx115_com_cn_sc.sc_iot;
        bus_space_handle_t  ioh = vx115_com_cn_sc.sc_ioh;

        /* spin while tx fifo full */
        while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF)
            ;
        
        /* write the next char */
        REG_WRITE(ASCC_TX_RX_FIFO, *str++);
    }
}
#endif


static int  vx115_com_match(struct device *, struct cfdata *, void *);
static void vx115_com_attach(struct device *, struct device *, void *);
static void vx115_com_attach_subr(struct vx115_com_softc *sc);
static int vx115_com_intr(void* arg);


CFATTACH_DECL(vx115_com, sizeof(struct vx115_com_softc),
    vx115_com_match, vx115_com_attach, NULL, NULL);


/*********************************************************************
 * Autoconfig functions
 */
 
static int
vx115_com_match(struct device *parent, struct cfdata *match, void *aux)
{
    COM_DEBUG_PRINT_STRING("vx115_com_match\n");
    
    if (strcmp(match->cf_name, "vx115_com") == 0)
        return 1;
    return 0;
}

static void
vx115_com_attach(struct device *parent, struct device *self, void *aux)
{
    struct vx115_com_softc *sc = (struct vx115_com_softc *)self;
    struct vx115_attach_args *sa = (struct vx115_attach_args *)aux;

    sc->sc_iot = sa->sa_iot;
    sc->sc_addr = sa->sa_addr;

    printf("\n");
    COM_DEBUG_PRINT_STRING("vx115_com_attach\n");

    bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh);

    vx115_com_attach_subr(sc);
    
    vx115_intr_establish(sa->sa_intr, IPL_SERIAL, vx115_com_intr, sc);
    vx115_configure_irq(sa->sa_intr, VX115_INT_SENSE_LEVEL, VX115_INT_POLARITY_HIGH);
    vx115_enable_irq(sa->sa_intr);
}



static void
vx115_com_attach_subr(struct vx115_com_softc *sc)
{
    struct tty *tp;

    COM_DEBUG_PRINT_STRING("vx115_com_attach_subr\n");
    
    /* check if this is com port for console */
    if ((sc->sc_iot == vx115_com_cn_sc.sc_iot) && (sc->sc_addr == vx115_com_cn_sc.sc_addr)) 
    {
        vx115_com_cn_sc.sc_attached = 1;
        
        //delay(10000);    /* wait for output to finish */
        
        /* make sure the console is always "hardwired" */
        SET(sc->sc_hwflags, COM_HW_CONSOLE);
        SET(sc->sc_swflags, TIOCFLAG_SOFTCAR);
    }

    tp = ttymalloc();
    tp->t_oproc = vx115_com_start;
    tp->t_param = vx115_com_param;
    tp->t_hwiflow = vx115_com_hwiflow;

    sc->sc_tty = tp;
    sc->sc_rbuf = malloc(VX115_COM_RING_SIZE << 1, M_DEVBUF, M_NOWAIT);
    sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
    sc->sc_rbavail = VX115_COM_RING_SIZE;
    if (sc->sc_rbuf == NULL) 
    {
        printf("%s: unable to allocate ring buffer\n", sc->sc_dev.dv_xname);
        return;
    }
    sc->sc_ebuf = sc->sc_rbuf + (VX115_COM_RING_SIZE << 1);
    sc->sc_tbc = 0;

    /* initalize control registers: 8-B-1 and appropriate baud rate */
    /* set FIFO thresholds to the halfway point */
    sc->sc_baud_divisor = VX115_BAUD_DIVISOR(DEFAULT_COMSPEED);
    sc->sc_rx_control = ASCC_RX_CTRL_RXCHSIZE_8 | ASCC_RX_CTRL_RXP_NO | ASCC_RX_CTRL_RXTH32 | ASCC_RX_CTRL_BRDEN;
    sc->sc_tx_control = ASCC_TX_CTRL_TXCHSIZE_8 | ASCC_TX_CTRL_TXP_NO | ASCC_TX_CTRL_TXTH32;
    
    /* clear disable bit, if set */
    sc->sc_rx_control &= ~ASCC_RX_CTRL_RXID;
    sc->sc_tx_control &= ~ASCC_TX_CTRL_TXID;
    
    /* also set the rx timeout count */
    sc->sc_char_counter_control = (0x100 & ASCC_CHAR_COUNTER_CONTROL_COUNT_MASK);
    
    tty_attach(tp);

    if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) 
    {
        int maj;

        /* locate the major number */
        maj = cdevsw_lookup_major(&vx115_com_cdevsw);
        cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
        aprint_normal("%s: major = %i: console\n", sc->sc_dev.dv_xname, maj);
    }

    sc->sc_softintr = softintr_establish(IPL_SOFTSERIAL, vx115_com_soft, sc);

#if NRND > 0 && defined(RND_COM)
    rnd_attach_source(&sc->rnd_source, sc->sc_dev.dv_xname,
              RND_TYPE_TTY, 0);
#endif

    /* if there are no enable/disable functions, assume the device is always enabled */
    if (!sc->enable)
        sc->enabled = 1;

    SET(sc->sc_hwflags, COM_HW_DEV_OK);
}





/************************************************************** 
 * cdevsw functions
 */

int
vx115_com_open(dev_t dev, int flag, int mode, struct proc *p)
{
    struct vx115_com_softc *sc;
    struct tty *tp;
    int s, s2;
    int error;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_open\n");
    
    sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK) || sc->sc_rbuf == NULL)
        return (ENXIO);

    if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0)
        return (ENXIO);

#ifdef KGDB
    /*
     * If this is the kgdb port, no other use is permitted.
     */
    if (ISSET(sc->sc_hwflags, COM_HW_KGDB))
        return (EBUSY);
#endif

    tp = sc->sc_tty;

    if (ISSET(tp->t_state, TS_ISOPEN) &&
        ISSET(tp->t_state, TS_XCLUDE) &&
        p->p_ucred->cr_uid != 0)
        return (EBUSY);

    s = spltty();

    /*
     * Do the following iff this is a first open.
     */
    if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) 
    {
        struct termios t;

        tp->t_dev = dev;

        s2 = splserial();
        COM_LOCK(sc);

        if (sc->enable) 
        {
            if ((*sc->enable)(sc)) 
            {
                COM_UNLOCK(sc);
                splx(s2);
                splx(s);
                printf("%s: device enable failed\n", sc->sc_dev.dv_xname);
                return (EIO);
            }
            sc->enabled = 1;
            
        }

        /* Turn on receive interrupts. */
        sc->sc_rx_control |= RECEIVE_INTERRUPT_MASK;
        sc->sc_char_counter_control |= CHAR_CNT_CNTL_INTERRUPT_MASK;
    
        vx115_com_set_cr(sc);

        COM_UNLOCK(sc);
        splx(s2);

        /*
         * Initialize the termios status to the defaults.  Add in the
         * sticky bits from TIOCSFLAGS.
         */
        t.c_ispeed = 0;
        if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) 
        {
            t.c_ospeed = vx115_com_cn_sc.sc_ospeed;
            t.c_cflag = vx115_com_cn_sc.sc_cflag;
        } 
        else 
        {
            t.c_ospeed = TTYDEF_SPEED;
            t.c_cflag = TTYDEF_CFLAG;
        }
        if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
            SET(t.c_cflag, CLOCAL);
        if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
            SET(t.c_cflag, CRTSCTS);
        if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
            SET(t.c_cflag, MDMBUF);
        
        /* Make sure vx115_com_param() will do something. */
        tp->t_ospeed = 0;
        (void) vx115_com_param(tp, &t);
        tp->t_iflag = TTYDEF_IFLAG;
        tp->t_oflag = TTYDEF_OFLAG;
        tp->t_lflag = TTYDEF_LFLAG;
        ttychars(tp);
        ttsetwater(tp);

        s2 = splserial();
        COM_LOCK(sc);

        /* Clear the input ring, and unblock. */
        sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
        sc->sc_rbavail = VX115_COM_RING_SIZE;
        vx115_com_iflush(sc);
        CLR(sc->sc_rx_flags, RX_ANY_BLOCK);

#ifdef COM_DEBUG
        if (vx115_com_debug)
            comstatus(sc, "vx115_com_open  ");
#endif

        COM_UNLOCK(sc);
        splx(s2);
    }
    
    splx(s);

    error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
    if (error)
        goto bad;

    error = (*tp->t_linesw->l_open)(dev, tp);
    if (error)
        goto bad;

    return (0);

bad:
    if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
        /*
         * We failed to open the device, and nobody else had it opened.
         * Clean up the state as appropriate.
         */
        vx115_com_shutdown(sc);
    }

    return (error);
}

int
vx115_com_close(dev, flag, mode, p)
    dev_t dev;
    int flag, mode;
    struct proc *p;
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_close\n");
    
    /* XXX This is for cons.c. */
    if (!ISSET(tp->t_state, TS_ISOPEN))
        return (0);

    (*tp->t_linesw->l_close)(tp, flag);
    ttyclose(tp);

    if (COM_ISALIVE(sc) == 0)
        return (0);

    if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
        /*
         * Although we got a last close, the device may still be in
         * use; e.g. if this was the dialout node, and there are still
         * processes waiting for carrier on the non-dialout node.
         */
        vx115_com_shutdown(sc);
    }

    return (0);
}

int
vx115_com_read(dev, uio, flag)
    dev_t dev;
    struct uio *uio;
    int flag;
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_read\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);
 
    return ((*tp->t_linesw->l_read)(tp, uio, flag));
}

int
vx115_com_write(dev, uio, flag)
    dev_t dev;
    struct uio *uio;
    int flag;
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_write\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);
 
    return ((*tp->t_linesw->l_write)(tp, uio, flag));
}

int
vx115_com_poll(dev, events, p)
    dev_t dev;
    int events;
    struct proc *p;
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_poll\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);
 
    return ((*tp->t_linesw->l_poll)(tp, events, p));
}

/* Stop output on a line. */
void
vx115_com_stop(struct tty *tp, int flag)
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev));
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_stop\n");
    
    s = splserial();
    COM_LOCK(sc);
    if (ISSET(tp->t_state, TS_BUSY)) 
    {
        /* Stop transmitting at the next chunk. */
        sc->sc_tbc = 0;
        sc->sc_heldtbc = 0;
        if (!ISSET(tp->t_state, TS_TTSTOP))
            SET(tp->t_state, TS_FLUSH);
    }
    COM_UNLOCK(sc);    
    splx(s);
}

struct tty *
vx115_com_tty(dev_t dev)
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tty\n");
    
    return (tp);
}

int
vx115_com_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;
    int error;
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_ioctl\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);

    error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p);
    if (error != EPASSTHROUGH)
        return (error);

    error = ttioctl(tp, cmd, data, flag, p);
    if (error != EPASSTHROUGH)
        return (error);

    error = 0;

    s = splserial();
    COM_LOCK(sc);    

    switch (cmd) {
    case TIOCSBRK:
        vx115_com_break(sc, 1);
        break;

    case TIOCCBRK:
        vx115_com_break(sc, 0);
        break;

    case TIOCGFLAGS:
        *(int *)data = sc->sc_swflags;
        break;

    case TIOCSFLAGS:
        error = suser(p->p_ucred, &p->p_acflag); 
        if (error)
            break;
        sc->sc_swflags = *(int *)data;
        break;

    default:
        error = EPASSTHROUGH;
        break;
    }

    COM_UNLOCK(sc);
    splx(s);

    return (error);
}



/**************************************************************
 * Utility functions
 */
 

/* Flush the hardware receive FIFO */
static void
vx115_com_iflush(struct vx115_com_softc *sc)
{
    bus_space_tag_t iot = sc->sc_iot;
    bus_space_handle_t ioh = sc->sc_ioh;
    int timo;
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_iflush\n");
    
    timo = 50000;
    /* flush any pending I/O */
    while ( !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE) && --timo )
    {
        REG_READ(ASCC_TX_RX_FIFO);
    }
    
#ifdef DIAGNOSTIC
    if (!timo)
        printf("%s: com_iflush timeout\n", sc->sc_dev.dv_xname);
#endif
}


/* Set or clear generation of break condition */
static void
vx115_com_break(struct vx115_com_softc *sc, int onoff)
{
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_break\n");
    
    if (onoff)
        sc->sc_tx_control |= ASCC_TX_CTRL_BREAK;
    else
        sc->sc_tx_control &= ~ASCC_TX_CTRL_BREAK;
        
    if (!sc->sc_heldchange) 
    {
        if (sc->sc_tx_busy) 
        {
            sc->sc_heldtbc = sc->sc_tbc;
            sc->sc_tbc = 0;
            sc->sc_heldchange = 1;
        } 
        else
        {
            vx115_com_set_cr(sc);
        }
    }
}


static void
vx115_com_shutdown(struct vx115_com_softc *sc)
{
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_shutdown\n");
    
    s = splserial();
    COM_LOCK(sc);    

    /* Clear any break condition set with TIOCSBRK. */
    vx115_com_break(sc, 0);

    /* Turn off interrupts. */
    sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK;
    sc->sc_tx_control &= ~TRANSMIT_INTERRUPT_MASK;
    sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK;
    /* set disable bit */
    sc->sc_rx_control |= ASCC_RX_CTRL_RXID;
    sc->sc_tx_control |= ASCC_TX_CTRL_TXID;
    
    vx115_com_set_cr(sc);

    if (sc->disable) {
#ifdef DIAGNOSTIC
        if (!sc->enabled)
            panic("vx115_com_shutdown: not enabled?");
#endif
        (*sc->disable)(sc);
        sc->enabled = 0;
    }
    COM_UNLOCK(sc);
    splx(s);
}


static u_int
cflag_to_rx_control(tcflag_t cflag)
{
    u_int cr = 0;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("cflag_to_rx_control\n");
    
    if (cflag & PARENB)
        cr |= (cflag & PARODD) ? ASCC_RX_CTRL_RXP_ODD : ASCC_RX_CTRL_RXP_EVEN;
    
    cr |= ((cflag & CSIZE) == CS8) ? ASCC_RX_CTRL_RXCHSIZE_8 : ASCC_RX_CTRL_RXCHSIZE_7;
    cr |= (cflag & CSTOPB) ? ASCC_RX_CTRL_RXSTOP2 : ASCC_RX_CTRL_RXSTOP1;
    
    return (cr);
}

static u_int
cflag_to_tx_control(tcflag_t cflag)
{
    u_int cr = 0;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("cflag_to_tx_control\n");
    
    if (cflag & PARENB)
        cr |= (cflag & PARODD) ? ASCC_TX_CTRL_TXP_ODD : ASCC_TX_CTRL_TXP_EVEN;
    
    cr |= ((cflag & CSIZE) == CS8) ? ASCC_TX_CTRL_TXCHSIZE_8 : ASCC_TX_CTRL_TXCHSIZE_7;
    cr |= (cflag & CSTOPB) ? ASCC_TX_CTRL_TXSTOP2 : ASCC_TX_CTRL_TXSTOP1;
    
    return (cr);
}


static void
vx115_com_set_cr(struct vx115_com_softc *sc)
{
    bus_space_tag_t iot = sc->sc_iot;
    bus_space_handle_t ioh = sc->sc_ioh;
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_set_cr\n");
    
    /* set control regs and baud divisor */
    REG_WRITE(ASCC_RX_CONTROL, sc->sc_rx_control);
    REG_WRITE(ASCC_TX_CONTROL, sc->sc_tx_control);
    REG_WRITE(ASCC_BAUD_DIVISOR, sc->sc_baud_divisor);
    REG_WRITE(ASCC_CHAR_COUNTER_CONTROL, sc->sc_char_counter_control);
}



/**************************************************************************
 * tty functions
 * Assigned to associated tty struct fields in vx115_com_open
 */
 
/* Assigned to standard termios line discipline t_linesw->l_start method.   */
/* Called from within our vx115_txsoft method.                              */
static void 
vx115_com_start(struct tty *tp)
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev));
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_start\n");
    
    if (COM_ISALIVE(sc) == 0)
        return;

    s = spltty();
    if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
        goto out;
    if (sc->sc_tx_stopped)
        goto out;

    if (tp->t_outq.c_cc <= tp->t_lowat) 
    {
        if (ISSET(tp->t_state, TS_ASLEEP)) 
        {
            CLR(tp->t_state, TS_ASLEEP);
            wakeup(&tp->t_outq);
        }
        selwakeup(&tp->t_wsel);
        if (tp->t_outq.c_cc == 0)
            goto out;
    }

    /* output the first contiguous region of buffer space. */
    /* set spl to splserial to prevent race condition on sc_tbc,sc_tba, sc_tx_busy */
    (void)splserial();
    COM_LOCK(sc);

    sc->sc_tba = tp->t_outq.c_cf;
    sc->sc_tbc = ndqb(&tp->t_outq, 0);
    
    if (sc->sc_tbc > 0)
    {
        COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_start: sc->sc_tbc > 0\n");
        
        SET(tp->t_state, TS_BUSY);
        sc->sc_tx_busy = 1;
    
        /* Enable transmit interrupts */
        sc->sc_tx_control |= TRANSMIT_INTERRUPT_MASK;
        vx115_com_set_cr(sc);
    }

    COM_UNLOCK(sc);
    
out:
    splx(s);
    return;
}


static int
vx115_com_param(struct tty *tp, struct termios *t)
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev));
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_param\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);

    if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
        return (EINVAL);

    sc->sc_baud_divisor = VX115_BAUD_DIVISOR(t->c_ospeed);

    /*
     * For the console, always force CLOCAL and !HUPCL, so that the port
     * is always active.
     */
    if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) || ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) 
    {
        SET(t->c_cflag, CLOCAL);
        CLR(t->c_cflag, HUPCL);
    }

    /*
     * If there were no changes, don't do anything.  This avoids dropping
     * input and improves performance when all we did was frob things like
     * VMIN and VTIME.
     */
    if ((tp->t_ospeed == t->c_ospeed) && (tp->t_cflag == t->c_cflag))
        return (0);

    sc->sc_rx_control |= cflag_to_rx_control(t->c_cflag);
    sc->sc_tx_control |= cflag_to_tx_control(t->c_cflag);
    
    s = splserial();
    COM_LOCK(sc);    
    
    /* don't use hardware flow control */

    /* copy to tty */
    tp->t_ispeed = 0;
    tp->t_ospeed = t->c_ospeed;
    tp->t_cflag = t->c_cflag;

    if (!sc->sc_heldchange) 
    {
        if (sc->sc_tx_busy) 
        {
            sc->sc_heldtbc = sc->sc_tbc;
            sc->sc_tbc = 0;
            sc->sc_heldchange = 1;
        } 
        else
            vx115_com_set_cr(sc);
    }

    COM_UNLOCK(sc);
    splx(s);

    /*
     * Update the tty layer's idea of the carrier bit.
     * We tell tty the carrier is always on.
     */
    (void) (*tp->t_linesw->l_modem)(tp, 1);

#ifdef COM_DEBUG
    if (com_debug)
        comstatus(sc, "comparam ");
#endif

    if (!ISSET(t->c_cflag, CHWFLOW)) {
        if (sc->sc_tx_stopped) {
            sc->sc_tx_stopped = 0;
            vx115_com_start(tp);
        }
    }

    return (0);
}



static int
vx115_com_hwiflow(tp, block)
    struct tty *tp;
    int block;
{
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_hwiflow\n");
    
    return (0);
}



/**************************************************************
 * Console functions
 */
 
int
vx115_com_cnattach(bus_space_tag_t iot, bus_addr_t iobase, bus_space_handle_t ioh, int ospeed, tcflag_t cflag)
{
    unsigned int baud_divisor, rx_control, tx_control;
    
    cn_tab = &vx115_com_cons;
    cn_init_magic(&vx115_com_cnm_state);
    //cn_set_magic("\047\001"); /* default magic is BREAK */
    
    vx115_com_cn_sc.sc_iot = iot;
    vx115_com_cn_sc.sc_ioh = ioh;
    vx115_com_cn_sc.sc_addr = iobase;
    vx115_com_cn_sc.sc_ospeed = ospeed;
    vx115_com_cn_sc.sc_cflag = cflag;

    baud_divisor = VX115_BAUD_DIVISOR(ospeed);
    rx_control = cflag_to_rx_control(cflag);
    tx_control = cflag_to_tx_control(cflag);
    
    /* set rx and tx to interrupt on threshold achievement, */
    /* and set thresholds to the halfway point              */
    rx_control |= ASCC_RX_CTRL_FICE_TH | ASCC_RX_CTRL_RXTH32;
    tx_control |= ASCC_TX_CTRL_FICE_TH | ASCC_TX_CTRL_TXTH32;
    
    /* clear disable bit, if set */
    rx_control &= ~ASCC_RX_CTRL_RXID;
    tx_control &= ~ASCC_TX_CTRL_TXID;
    
    /* enable the UART; note that interrupts will not yet be hooked up       */
    /* however, the kernel printf function depends only on vx115_com_cnputc, */
    /* which doesn't require that interrupts be ready, so we get output      */
    bus_space_write_4(iot, ioh, ASCC_BAUD_DIVISOR, baud_divisor);
    bus_space_write_4(iot, ioh, ASCC_RX_CONTROL, rx_control);
    bus_space_write_4(iot, ioh, ASCC_TX_CONTROL, tx_control);
    
    return (0);
}


static void
vx115_com_cnpollc(dev, on)
    dev_t dev;
    int on;
{
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cnpollc\n");
}


static void
vx115_com_cnputc(dev_t dev, int c)
{
    int s;
    bus_space_tag_t     iot = vx115_com_cn_sc.sc_iot;
    bus_space_handle_t  ioh = vx115_com_cn_sc.sc_ioh;

    //COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cnputc\n");
    
    s = splserial();

    /* spin while tx fifo full */
    while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF)
        ;
    
    /* write the char */
    REG_WRITE(ASCC_TX_RX_FIFO, c);

    splx(s);
}


static int
vx115_com_cngetc(dev_t dev)
{
    int c;
    int s;
    bus_space_tag_t     iot = vx115_com_cn_sc.sc_iot;
    bus_space_handle_t  ioh = vx115_com_cn_sc.sc_ioh;

    //COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cngetc\n");
    
    s = splserial();

    /* spin while rx fifo empty */
    while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE)
        ;

    /* get the char */
    c = REG_READ(ASCC_TX_RX_FIFO);
    c &= 0xff;
    
    splx(s);

    return (c);
}



/**************************************************************
 * Soft interrupt functions
 */


inline static void
vx115_com_tx_soft(struct vx115_com_softc *sc, struct tty *tp)
{
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tx_soft\n");
    
    CLR(tp->t_state, TS_BUSY);
    
    /* adjust tty buffer pointers to account for data that's been sent */
    if (ISSET(tp->t_state, TS_FLUSH))
        CLR(tp->t_state, TS_FLUSH);
    else
        ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
    
    /* for the standard tty line discipline, just calls vx115_com_start;    */
    /* this will check to see if any new data's available, and set transmit */
    /* interrupt if so to begin transmission                                */
    (*tp->t_linesw->l_start)(tp);
}



inline static void
vx115_com_rx_soft(struct vx115_com_softc *sc, struct tty *tp)
{
    int (*rint) __P((int c, struct tty *tp)) = tp->t_linesw->l_rint;
    u_char *get, *end;
    u_int cc, scc;
    u_char lsr;
    int code;
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_rx_soft\n");
    
    end = sc->sc_ebuf;
    get = sc->sc_rbget;
    scc = cc = VX115_COM_RING_SIZE - sc->sc_rbavail;
    

    while (cc) 
    {
        code = get[0];
        lsr  = get[1];
        
        if (ISSET(lsr, (ASCC_TX_RX_FIFO_ERROR_RPE_RFE | ASCC_TX_RX_FIFO_ERROR_BRE)))
        {
            if (ISSET(lsr, ASCC_TX_RX_FIFO_ERROR_RPE_RFE))
            {
                /* we have either a parity or framing error; unfortunately, */
                /* the ASCC doesn't tell us which, so just call it parity   */
                SET(code, TTY_PE);
            }
            
            if (ISSET(lsr, ASCC_TX_RX_FIFO_ERROR_BRE))
            {
                /* break condition is reported through TTY_FE */
                SET(code, TTY_FE);
            }
        }
        
        if ((*rint)(code, tp) != 0) 
        {
            /* error; not sure what to do... */
        }
        
        get += 2;
        if (get >= end)
            get = sc->sc_rbuf;
        cc--;
    }

    if (cc != scc) 
    {
        sc->sc_rbget = get;
        s = splserial();
        COM_LOCK(sc);
        
        sc->sc_rbavail += scc - cc;
        
        if (sc->sc_rbavail >= 1) 
        {
            /* buffers should be ok again, release possible block */
            if (ISSET(sc->sc_rx_flags, RX_IBUF_FULL)) 
            {
                CLR(sc->sc_rx_flags, RX_IBUF_FULL);
                
                /* enable receive interrupts */
                sc->sc_rx_control |= RECEIVE_INTERRUPT_MASK;
                sc->sc_char_counter_control |= CHAR_CNT_CNTL_INTERRUPT_MASK;
                vx115_com_set_cr(sc);
            }
            
        }
        
        COM_UNLOCK(sc);
        splx(s);
    }
}


static void
vx115_com_soft(void* arg)
{
    struct vx115_com_softc *sc = arg;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_soft\n");
    
    if (COM_ISALIVE(sc) == 0)
        return;

    if (sc->sc_rx_ready) 
    {
        sc->sc_rx_ready = 0;
        vx115_com_rx_soft(sc, sc->sc_tty);
    }
    
    if (!sc->sc_tx_busy) 
    {
        /* previous transmissions complete; see if have any new stuff to send */
        vx115_com_tx_soft(sc, sc->sc_tty);
    }
}




/**************************************************************
 * Hardware interrupt functions
 */

static void
vx115_com_rx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh)
{
    u_char *put, *end;
    u_int cc;
    u_int32_t c;
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_rx_hard\n");
    
    if (ISSET(sc->sc_rx_flags, RX_IBUF_FULL)) 
    {
        /* overflow - turn off interrupts */
        sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK;
        sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK;
        vx115_com_set_cr(sc);
        
        return;
    }
    
    /* get receive buffer parameters */
    end = sc->sc_ebuf;
    put = sc->sc_rbput;
    cc = sc->sc_rbavail;
        
    /* copy characters from ASCC FIFO to receive buffer */
    /* while FIFO not empty and buffer not full; also   */
    /* check for magic character sequence               */
    while ((cc > 0) && !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE))
    {
        c = REG_READ(ASCC_TX_RX_FIFO);
        put[0] = c & 0xff;
        put[1] = (c >> 8) & 0xff;
        cn_check_magic(sc->sc_tty->t_dev, put[0], vx115_com_cnm_state);
        put += 2;
        if (put >= end)
            put = sc->sc_rbuf;
        cc--;
    }

    /*
     * Current string of incoming characters ended because
     * no more data was available or we ran out of space.
     * Schedule a receive event if any data was received.
     * If we're out of space, turn off receive interrupts.
     */
    sc->sc_rbput = put;
    sc->sc_rbavail = cc;
    sc->sc_rx_ready = 1;

    /*
     * If we're out of space, disable receive interrupts
     * until the queue has drained a bit.
     */
    if (cc == 0) 
    {
        SET(sc->sc_rx_flags, RX_IBUF_FULL);
        sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK;
        sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK;
        vx115_com_set_cr(sc);
    }        
}



static void
vx115_com_tx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh)
{
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tx_hard\n");
    
    /* If we've delayed a parameter change, do it now, and restart output. */
    if (sc->sc_heldchange) 
    {
        vx115_com_set_cr(sc);
        sc->sc_heldchange = 0;
        sc->sc_tbc = sc->sc_heldtbc;
        sc->sc_heldtbc = 0;
    }

    
    
    /* output the next chunk of the contiguous buffer, if any. */
    if (sc->sc_tbc > 0) 
    {
        int n = 0;
        
        while ( !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF) )
        {
            if (n >= sc->sc_tbc)
                break;
            REG_WRITE(ASCC_TX_RX_FIFO, 0xff & *(sc->sc_tba + n));
            n++;
        }
        sc->sc_tbc -= n;
        sc->sc_tba += n;
    }
    
    if (sc->sc_tbc == 0) 
    {
        /* we've finished with transmit buffer; disable transmit completion interrupts; */
        /* will be reenabled when vx115_com_start called to transmit more  */
        sc->sc_tx_control &= ~TRANSMIT_INTERRUPT_MASK;
        vx115_com_set_cr(sc);
        sc->sc_tx_busy = 0;
    }
}


static int
vx115_com_intr(void* arg)
{
    struct vx115_com_softc *sc = arg;
    bus_space_tag_t    iot = sc->sc_iot;
    bus_space_handle_t ioh = sc->sc_ioh;
    u_int status;
    
    if (COM_ISALIVE(sc) == 0)
        return (0);

    COM_LOCK(sc);
    
    /* read main status reg to see what's up */
    status = REG_READ(ASCC_STATUS);
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("i\n");

    do 
    {

        if (status & ASCC_STATUS_ROE)
        {
            /* receive overrun */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("o\n");
        }       

        if (status & ASCC_STATUS_RPE)
        {
            /* parity error */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("p\n");
        }       

        if (status & ASCC_STATUS_RFE)
        {
            /* framing error */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("f\n");
        }       

        if (status & ASCC_STATUS_MISC_ANY)
        {
            /* it's a modem status interrupt */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("m\n");
        }  

        if (status & (ASCC_STATUS_RFT | ASCC_STATUS_CCE0))
        {
            /* it's a receive FIFO interrupt; either hit */
            /* threshold, or timed out with chars avail  */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("r\n");
            vx115_com_rx_hard(sc, iot, ioh);
        }

        if (status & ASCC_STATUS_TFT)
        {
            /* it's a transmit FIFO interrupt */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("t\n");
            vx115_com_tx_hard(sc, iot, ioh);
        }
        
        /* see if we're done */
        status = REG_READ(ASCC_STATUS);

    } while (status);

    COM_UNLOCK(sc);

    /* Wake up the poller. */
    softintr_schedule(sc->sc_softintr);

    return (1);
}
