ПРОЕКТЫ 


  АРХИВ 


Apache-Talk @lexa.ru 

Inet-Admins @info.east.ru 

Filmscanners @halftone.co.uk 

Security-alerts @yandex-team.ru 

nginx-ru @sysoev.ru 

  СТАТЬИ 


  ПЕРСОНАЛЬНОЕ 


  ПРОГРАММЫ 



ПИШИТЕ
ПИСЬМА












     АРХИВ :: Inet-Admins
Inet-Admins mailing list archive (inet-admins@info.east.ru)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[inet-admins] Re: digi FEP/OS start failed


  • To: Sergey Samoyloff <gonza@techline.ru>
  • Subject: [inet-admins] Re: digi FEP/OS start failed
  • From: Slawa Olhovchenkov <slw@convey.ru>
  • Date: Sun, 5 Mar 2000 03:12:38 +0300
  • In-Reply-To: <200003041728.UAA29528@hq.techline.ru>
  • References: <20000304201216.A13792@as.convey.ru> <200003041728.UAA29528@hq.techline.ru>

On Sat, Mar 04, 2000 at 08:28:13PM +0300, Sergey Samoyloff wrote:
> Hello!
> 
> >> Слав сразу не заметил, оно на fepos стартануть не может, ругается что
> >> digi0: FEP/OS start failed: got 0000 wait 534f
> >Попробуй в строке 1413 заменить 2000 на 2000000
> 
> взвисла
 Ничего не понимаю. А это вариант?


-- 
Slawa Olhovchenkov
/*-
 * Copyright (c) 2000 Slawa Olhovchenkov
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 *
 *	$Id: digi.c,v 1.5 2000/03/04 21:13:35 slw Exp $
 */

#include "pci.h"
#include "digi.h"
#include "opt_devfs.h"
#include "opt_compat.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/reboot.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/dkstat.h>
#include <sys/file.h>
#include <sys/uio.h>
#if __FreeBSD__ > 3
#include <sys/interrupt.h>
#endif
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <sys/syslog.h>
#if defined(DEVFS) && __FreeBSD__ <= 3 
#include <sys/devfsext.h>
#endif
#include <sys/types.h>
#include <sys/fcntl.h>

#include <machine/clock.h>

#if __FreeBSD__ <= 3
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/icu.h>
#else
#include <sys/bus.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/timepps.h>
#include <isa/isareg.h>
#include <isa/isavar.h>
#include <machine/resource.h>
#endif

#include <vm/vm.h>
#include <vm/pmap.h>

#include <i386/isa/digireg.h>

#include <machine/ipl.h>
#ifndef SMP
#include <machine/lock.h>
#endif

static const char rcsid[] = "$Id: digi.c,v 1.5 2000/03/04 21:13:35 slw Exp $";

#define	DOWNLOAD_MASK		0x800000
#define	CALLOUT_MASK		0x400000
#define	CONTROL_INIT_STATE	0x100000
#define	CONTROL_LOCK_STATE	0x200000
#define	CONTROL_MASK		(DOWNLOAD_MASK|CONTROL_INIT_STATE|CONTROL_LOCK_STATE)
#define UNIT_MASK		0x030000
#define PORT_MASK		0x0000FF
#define	DEV_TO_UNIT(dev)	(MINOR_TO_UNIT(minor(dev)))
#define	MINOR_MAGIC_MASK	(CALLOUT_MASK | CONTROL_MASK)
#define	MINOR_TO_UNIT(mynor)	(((mynor) & UNIT_MASK)>>16)
#define MINOR_TO_PORT(mynor)	((mynor) & PORT_MASK)

#ifdef SMP
#define disable_intr()		COM_DISABLE_INTR()
#define enable_intr()		COM_ENABLE_INTR()
#endif /* SMP */

static	char const * const	error_desc[] = {
#define	CE_OVERRUN			0
	"silo overflow",
#define	CE_INTERRUPT_BUF_OVERFLOW	1
	"interrupt-level buffer overflow",
#define	CE_TTY_BUF_OVERFLOW		2
	"tty-level buffer overflow",
};

#define	CE_NTYPES			3
#define	CE_RECORD(com, errnum)		(++(com)->delta_error_counts[errnum])

/* digiboard port structure */
struct digi_p {
	int	status;
#define DIGI_DTR_OFF 2
#define PAUSE_TX 8
#define PAUSE_RX 16
	int opencnt;
	ushort txbufsize;
	ushort rxbufsize;
	struct board_chan *bc;
	struct tty *tp;
#if __FreeBSD__ <= 3 && defined(DEVFS)
        void *devfs_token_ttyd,*devfs_token_ttyl,*devfs_token_ttyi,
	     *devfs_token_cuaa,*devfs_token_cual,*devfs_token_cuai;
#endif
	u_char *txbuf;
	u_char *rxbuf;
	u_char txwin;
	u_char rxwin;

	u_char unit;           /* board unit number */
	u_char pnum;           /* port number */

	u_char modemfake;      /* Modem values to be forced   */
	u_char mstat;
	u_char modem;          /* Force values                */

	u_char dsr;
	u_char dcd;

	int     active_out;	/* nonzero if the callout device is open */
	int	dtr_wait;	/* time to hold DTR down on close (* 1/hz) */
	u_int	wopeners;	/* # processes waiting for DCD in open() */

	/*
	 * The high level of the driver never reads status registers directly
	 * because there would be too many side effects to handle conveniently.
	 * Instead, it reads copies of the registers stored here by the
	 * interrupt handler.
	 */
	u_char	last_modem_status;	/* last MSR read by intr handler */
	u_char	prev_modem_status;	/* last MSR handled by high level */


	/* Initial state. */
	struct termios	it_in;	/* should be in struct tty */
	struct termios	it_out;

	/* Lock state. */
	struct termios	lt_in;	/* should be in struct tty */
	struct termios	lt_out;

	u_int do_timestamp;
        u_int do_dcd_timestamp;
        struct timeval dcd_timestamp;

	u_long bytes_in, bytes_out;
	u_int delta_error_counts[CE_NTYPES];
	u_long error_counts;
	
	tcflag_t c_iflag;	/* hold true IXON/IXOFF/IXANY */
	int lcc,lostcc,lbuf;
	u_char send_ring;
};

typedef struct {
    unsigned int from, to;
} Bitmap;

Bitmap DigiConc[]={{0x01, TIOCM_DTR}, {0x02, TIOCM_RTS}, {0x10, TIOCM_CTS},
		   {0x20, TIOCM_DSR}, {0x40, TIOCM_RI}, {0x80, TIOCM_CD},
		   {0, 0}};

Bitmap DigiXi[]={{0x02, TIOCM_RTS}, {0x08, TIOCM_CD}, {0x10, TIOCM_DSR},
		 {0x20, TIOCM_CTS}, {0x40, TIOCM_RI}, {0x80, TIOCM_DTR}, 
		  {0, 0}};


/* Digiboard per-board structure */
struct digi_softc {
	/* struct board_info */
	u_char status;	/* status: DISABLED/ENABLED */
    
#define NOTINIT 0
#define ENABLED 1
#define DISABLED 2
    
	ushort numports,maxport;	/* number of ports on card */
	u_long port;	/* I/O port */
	ushort wport;	/* window select I/O port */
	u_char *vmem, *memcmd, *memevent; /* virtual memory address */
	long pmem, psize; /* physical memory address */
	u_char *bios,*fep,*link;
	int s_bios,s_fep,s_link;
	struct timeval intr_timestamp;
        void (*intr_handler)(int unit);
	struct digi_p *ports;	/* pointer to array of port descriptors */
	struct global_data *gdata;
	u_char window; /* saved window */
        int win_size, mem_size, mem_seg;
#define NONE 0
#define PCXE 1
#define PCXI 2
#define PCXR 3
#define PCXM 4
#define PCXEVE 5
#define PCXEM 6
#define PCCX 7
#define PCEPC 8
#define PCI_MODEL 0x80
        int model;
        Bitmap *bitmap;
	int opencnt;
	int unit;
#if __FreeBSD__ <= 3
#ifdef DEVFS
        void *devfs_token_ctl;
#endif
	struct tty *ttys;	/* pointer to array of TTY structures */
#else
	struct resource *port_res, *mem_res, *irq_res;
	int prid, mrid, irid;
	void *ih;
#endif
};

struct con_bios {
    struct con_bios *next;
    u_char *bios;
    size_t size;
};

struct con_bios *con_bios_list;

/* Device switch entry points. */
static d_open_t digiopen;
static d_close_t digiclose;
static d_read_t digiread;
static d_write_t digiwrite;
static d_ioctl_t digiioctl;
#if __FreeBSD__ <= 3
static d_stop_t digistop;
static d_devtotty_t digidevtotty;

static	int	digi_isa_attach	__P((struct isa_device *dev));
static	int	digi_isa_probe	__P((struct isa_device *dev));
#else
static	int	digi_isa_attach	__P((device_t dev));
static	int	digi_isa_probe	__P((device_t dev));
static  void    digistop	__P((struct tty *tp, int rw));
#endif
static  int digimctl __P((struct digi_p *port, int bits, int how));

static void fepcmd(struct digi_p *port, int cmd, int op, int ncmds);

#define fepcmd_b(p, cmd, o1, o2, ncmds) fepcmd(p, cmd, (o2<<8)|o1, ncmds)
#define fepcmd_w fepcmd

#define W(p)(*(ushort *)(p))

static	void	digistart	__P((struct tty *tp));
static	int	digiparam	__P((struct tty *tp, struct termios *t));
static void digihardclose	__P((struct digi_p *port));

static char driver_name[] = "digi";

#define CDEV_MAJOR 200

#if __FreeBSD__ <= 3
static struct cdevsw digi_cdevsw = {
  digiopen, digiclose, digiread, digiwrite, digiioctl, digistop, noreset,
  digidevtotty,
#if __FreeBSD__ == 2
  ttselect,
#else
  ttpoll,
#endif
  nommap, NULL, driver_name, NULL, -1,
#if __FreeBSD__ > 2
  nodump, nopsize, D_TTY,
#endif  
};

struct isa_driver	digidriver = {
	digi_isa_probe, digi_isa_attach, driver_name
};

static u_long digi_unit; /* For EISA and PCI */
static  struct digi_softc digi_softc[NDIGI];

static digi_devsw_installed = 0;

static void 	digi_drvinit(void *unused)
{
	dev_t dev;

	if( ! digi_devsw_installed ) {
		dev = makedev(CDEV_MAJOR, 0);
		cdevsw_add(&dev,&digi_cdevsw, NULL);
		digi_devsw_installed = 1;
    	}
}

SYSINIT(digidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,digi_drvinit,NULL)
#else
static  int     digi_numunits;

static devclass_t	digi_devclass;

static device_method_t digi_isa_methods[] = {
    /* Device interface */
    DEVMETHOD(device_probe,         digi_isa_probe),
    DEVMETHOD(device_attach,        digi_isa_attach),
    { 0, 0 }
};

static driver_t digi_isa_driver = {
    driver_name,
    digi_isa_methods,
    sizeof(struct digi_softc),
};

static struct cdevsw digi_cdevsw = {
        /* open */      digiopen,
        /* close */     digiclose,
        /* read */      digiread,
        /* write */     digiwrite,
        /* ioctl */     digiioctl,
        /* poll */      ttypoll,
        /* mmap */      nommap,
        /* strategy */  nostrategy,
        /* name */      driver_name,
        /* maj */       CDEV_MAJOR,
        /* dump */      nodump,
        /* psize */     nopsize,
        /* flags */     D_TTY,
        /* bmaj */      -1
};

DRIVER_MODULE(digi, isa, digi_isa_driver, digi_devclass, 0, 0);
#endif

static	speed_t	digidefaultrate = TTYDEF_SPEED;

static	struct speedtab digispeedtab[] = {
	{ 0,	0 }, /* old (sysV-like) Bx codes */
	{ 50,	1 },
	{ 75,	2 },
	{ 110,	3 },
	{ 134,	4 },
	{ 150,	5 },
	{ 200,	6 },
	{ 300,	7 },
	{ 600,	8 },
	{ 1200,	9 },
	{ 1800,	10 },
	{ 2400,	11 },
	{ 4800,	12 },
	{ 9600,	13 },
	{ 19200,	14 },
	{ 38400,	15 },
	{ 57600,	(02000 | 1) },
	{ 76800,	(02000 | 2) },
	{ 115200,	(02000 | 3) },
	{ 230400,	(02000 | 6) },
	{ -1,	-1 }
};

#define DEBUG_INIT 1
#define DEBUG_OPEN 2
#define DEBUG_CLOSE 4
#define DEBUG_SET 8
#define DEBUG_INT 16
#define DEBUG_READ 32
#define DEBUG_WRITE 64
#define DEBUG_RX 128
#define DEBUG_TX 256
#define DEBUG_IRQ 512
#define DEBUG_MODEM 0x400
#define DEBUG_RI 0x800

static int debug=0;

#define PCIPORT sc->vmem[0x200000]

static int setwin(struct digi_softc *sc, unsigned addr);
static void hidewin(struct digi_softc *sc);

static __inline int
setwin(sc,addr)
     struct digi_softc *sc;
     unsigned int addr;
{
    if(sc->wport == 0) return addr;
    outb(sc->wport, sc->window=FEPWIN|(addr/sc->win_size));
    return (addr % sc->win_size);
}

static __inline void
hidewin(sc)
     struct digi_softc *sc;
{
    if(sc->wport == 0) return ;
    outb(sc->wport, sc->window=0);
    outb(sc->port, 0);
}

static __inline void
towin(struct digi_softc *sc, int win)
{
    if(sc->wport == 0) return ;
    outb(sc->wport, sc->window=win);
}


#if __FreeBSD__ >= 3
void digiintr(int);
#else
inthand2_t digiintr;
#endif
static int digi_attach(int unit);

#include "eisa.h"
#if NEISA > 0

#define EISA_DEVICE_ID_DIGI_CX		0x10490102
#define EISA_DEVICE_ID_DIGI_PCXem	0x10490201
#define EISA_DEVICE_ID_DIGI_EPCX	0x10490301

#if __FreeBSD__ <= 3
#include <i386/eisa/eisaconf.h>

static int	digi_eisa_probe(void);
static int	digi_eisa_attach(struct eisa_device *e_dev);

static struct eisa_driver digi_eisa_driver = {
	"digi",
	digi_eisa_probe,
	digi_eisa_attach,
	NULL, /*shutdown*/
	&digi_unit
};

DATA_SET (eisadriver_set, digi_eisa_driver);
#else
#include <dev/eisa/eisaconf.h>

static device_method_t digi_eisa_methods[] = {
    /* Device interface */
    DEVMETHOD(device_probe,         digi_eisa_probe),
    DEVMETHOD(device_attach,        digi_eisa_attach),
    { 0, 0 }
};

static driver_t digi_eisa_driver = {
    driver_name,
    digi_eisa_methods,
    sizeof(struct digi_softc),
};

DRIVER_MODULE(digi, isa, digi_eisa_driver, digi_devclass, 0, 0);
#endif

static const char *digi_eisa_match(eisa_id_t type);

static const char*
digi_eisa_match(eisa_id_t type)
{
	switch(type) {
		case EISA_DEVICE_ID_DIGI_CX:
			return ("Digi EISA C/X host adapter");
			break;
		case EISA_DEVICE_ID_DIGI_PCXem:
			return ("Digi EISA PC/Xem host adapter");
			break;
		case EISA_DEVICE_ID_DIGI_EPCX:
			return ("Digi EISA EPC/X host adapter");
			break;
		default:
			break;
	}
	return (NULL);
}


#if __FreeBSD__ <= 3
static int
digi_eisa_probe(void)
{
	u_long iobase;
	struct eisa_device *e_dev = NULL;
	int count;
	static int irq_map[]={0,3,5,7,10,11,12,15};

	count = 0;
	while ((e_dev = eisa_match_dev(e_dev, digi_eisa_match))) {
		u_long port,maddr;
		u_int	irq;

		iobase = (e_dev->ioconf.slot * EISA_SLOT_SIZE); port= iobase+5;
		eisa_add_iospace(e_dev, port, 10, RESVADDR_NONE);
		irq = inb(port+2) & 0x0F;
		if (irq) eisa_add_intr(e_dev, irq_map[irq]);
		maddr = ((inb(port+2) & 0x80) << 8) | ((inb(port+3) & 0xFF) << 16) | ((inb(port+4) & 0xFF) << 24);
		eisa_add_mspace(e_dev, maddr, 32768, RESVADDR_NONE); /* EISA C/X, EISA PC/Xem and EISA EPC/X have 32K window */
		eisa_registerdev(e_dev, &digi_eisa_driver);
		count++;
	}
	return count;
}

static int
digi_eisa_attach(struct eisa_device *e_dev)
{
	int unit = e_dev->unit;
	int irq;
	resvaddr_t *ioport,*maddr;
	struct digi_softc *sc;

	sc= &digi_softc[unit];
	bzero(sc, sizeof(struct digi_softc));
	sc->status= DISABLED;

	if (TAILQ_FIRST(&e_dev->ioconf.irqs) == NULL)
		return (-1);

	irq = TAILQ_FIRST(&e_dev->ioconf.irqs)->irq_no;

	ioport = e_dev->ioconf.ioaddrs.lh_first;
	maddr = e_dev->ioconf.maddrs.lh_first;

	if (ioport == NULL)
		return -1;
	eisa_reg_start(e_dev);
	if (eisa_reg_iospace(e_dev, ioport))
		return -1;
	if (eisa_reg_iospace(e_dev, maddr))
		return -1;
	if (eisa_reg_intr(e_dev, irq, (void *)digiintr, (void *)unit, &tty_imask, 1))
		return -1;
	eisa_reg_end(e_dev);

	if (eisa_enable_intr(e_dev, irq)) {
		eisa_release_intr(e_dev, irq, (void *)digiintr);
		return -1;
	}
	sc->status=NOTINIT;
	switch(e_dev->id){
	case EISA_DEVICE_ID_DIGI_CX:
	    sc->maxport=64;
	    sc->model = PCCX;
	    break;
	case EISA_DEVICE_ID_DIGI_PCXem:
	    sc->maxport=128;
	    sc->model = PCXEM;
	    break;
	case EISA_DEVICE_ID_DIGI_EPCX:
	    sc->maxport=224;
	    sc->model = PCEPC;
	    break;
	}
	sc->port=(e_dev->ioconf.slot * EISA_SLOT_SIZE) + 5;
	sc->wport=sc->port+1;
	sc->win_size=32768;
	sc->mem_size=0;
	sc->mem_seg=0;
	sc->pmem= maddr->addr;
	sc->psize= 32768;
	sc->vmem= (caddr_t) pmap_mapdev(maddr->addr, 32768);
	return digi_attach(unit);
}
#else /* FreeBSD 4 */
static int
digi_eisa_probe(device_t dev)
{
	u_long iobase, port, maddr;
	u_int	irq;
	static int irq_map[]={0,3,5,7,10,11,12,15};
	const char *desc;
	struct digi_softc *sc= device_get_softc(dev);

	bzero(sc, sizeof(*sc));
	sc->status=DISABLED;
	desc = digi_eisa_match(eisa_get_id(dev));
	if(!desc) return ENXIO;
	sc->status=NOTINIT;
	device_set_desc(dev, desc);
	iobase = eisa_get_slot(dev) * EISA_SLOT_SIZE; port= iobase+5;
	eisa_add_iospace(dev, port, 10, RESVADDR_NONE);
	irq = inb(port+2) & 0x0F;
	if (irq) eisa_add_intr(dev, irq_map[irq], EISA_TRIGGER_LEVEL); /* XXX */
	maddr = ((inb(port+2) & 0x80) << 8) | ((inb(port+3) & 0xFF) << 16) | ((inb(port+4) & 0xFF) << 24);
	eisa_add_mspace(dev, maddr, 32768, RESVADDR_NONE); /* EISA C/X, EISA PC/Xem and EISA EPC/X have 32K window */
	log(LOG_INFO,"digi%d: irq %d pmem 0x%lx\n", device_get_unit(dev), irq_map[irq], maddr);
	switch(eisa_get_id(dev)){
	case EISA_DEVICE_ID_DIGI_CX:
	    sc->maxport=64;
	    sc->model = PCCX;
	    break;
	case EISA_DEVICE_ID_DIGI_PCXem:
	    sc->maxport=128;
	    sc->model = PCXEM;
	    break;
	case EISA_DEVICE_ID_DIGI_EPCX:
	    sc->maxport=224;
	    sc->model = PCEPC;
	    break;
	}
	sc->port=port;
	sc->wport=sc->port+1;
	sc->win_size=32768;
	sc->mem_size=0;
	sc->mem_seg=0;
	sc->pmem= maddr;
	sc->psize= 32768;
	return 0;
}

static int
digi_eisa_attach(device_t dev)
{
	int irq;
	void *ih;
	int unit=device_get_unit(dev);
	struct digi_softc *sc= device_get_softc(dev);

	sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->prid, 0, ~0, 5, RF_ACTIVE);
	if (!sc->port_res) return ENXIO;
	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mrid, 0, ~0, 1, RF_ACTIVE);
	if (!sc->mem_res) return ENXIO;
#if 0 /* Memory detected by eisa_probe */
        sc->pmem = (caddr_t)rman_get_start(sc->sc_mem_res);
	sc->psize = rman_get_end(sc->sc_mem_res) - sc->pmem + 1;
#endif
	sc->vmem = rman_get_virtual(sc->mem_res);
	return digi_attach(unit);
}

static device_method_t digi_eisa_methods[] = {
	DEVMETHOD(device_probe,	digi_eisa_probe),
	DEVMETHOD(device_attach,	digi_eisa_attach),
	{ 0, 0 }
};

static driver_t digi_eisa_driver = {
	driver_name,
	digi_eisa_driver,
	sizeof(struct digi_softc),
};

DRIVER_MODULE(digi, eisa, digi_eisa_driver, digi_devclass, 0, 0);
#endif
#endif /* NEISA > 0 */


#if NPCI > 0

#include <pci/pcivar.h>
#include <pci/pcireg.h>

#if __FreeBSD__ <= 3
static const char *digi_probe_pci __P((pcici_t, pcidi_t));
static void digi_attach_pci __P((pcici_t, int));

static struct pci_device digi_driver_pci = {
            "digi", digi_probe_pci, digi_attach_pci, &digi_unit, NULL
	    };
DATA_SET(pcidevice_set, digi_driver_pci);

static const char *
digi_probe_pci(config_id, device_id)
    pcici_t config_id;
    pcidi_t device_id;
{
    if ((device_id & 0xffff) == 0x114f){
	switch(device_id >> 16){
	case 0x0002:
	    return("Digi PCI EPC/X ASIC Version Serial Adapter");
	case 0x0004:
	    return("Digi PCI PC/Xem ASIC Version Serial Adapter");
	case 0x0005:
	    return("Digi PCI PC/Xr ASIC Version Serial Adapter");
	case 0x0006:
	    return("Digi PCI C/X ASIC Version Serial Adapter");
	case 0x000a:
	    return("Digi PCI EPC/X PLX Version Serial Adapter");
	case 0x0009:
	    return("Digi PCI PC/Xr PLX Version Serial Adapter");
	}
    }
    return NULL;
}

static void
digi_attach_pci(config_id, unit)
    pcici_t config_id;
    int unit;
{
    struct digi_softc *sc;
    pcidi_t device_id;
    int memreg, irq;

    sc= &digi_softc[unit];
    bzero(sc, sizeof(*sc));
    sc->status=DISABLED;
    pci_conf_write(config_id, 0x40,0);
    pci_conf_write(config_id, 0x46,0);
    pci_conf_write(config_id, 0x42,1);
    device_id = pci_conf_read(config_id, 0); /* Conf. ID */
    irq = pci_conf_read(config_id, PCI_INTERRUPT_REG) & 0xff;
    switch (device_id>>16) {
    case 0x0002:
	sc->maxport=224;
	memreg = 0x10;
	sc->model = PCEPC|PCI_MODEL;
	sc->win_size=2*1024*1024;
	sc->mem_size=sc->win_size; /* XXX */
	break;
    case 0x0004:
	sc->maxport=128;
	memreg = 0x10;
	sc->model = PCXEM|PCI_MODEL;
	sc->win_size=256*1024;
	sc->mem_size=sc->win_size; /* XXX */
	break;
    case 0x0005:
	sc->maxport=8;
	memreg = 0x10;
	sc->model = PCXR|PCI_MODEL;
	sc->win_size=128*1024;
	sc->mem_size=sc->win_size; /* XXX */
	break;
    case 0x0006:
	sc->maxport=64;
	memreg = 0x10;
	sc->model = PCCX|PCI_MODEL;
	sc->win_size=1024*1024;
	sc->mem_size=sc->win_size; /* XXX */
	break;
    case 0x000a:
	sc->maxport=224;
	memreg = 0x18;
	sc->model = PCEPC|PCI_MODEL;
	sc->win_size=2*1024*1024;
	sc->mem_size=sc->win_size; /* XXX */
	break;
    case 0x0009:
	sc->maxport=8;
	memreg = 0x18;
	sc->model = PCXR|PCI_MODEL;
	sc->win_size=128*1024;
	sc->mem_size=sc->win_size; /* XXX */
	break;
    default: 
	sc->status=DISABLED;
	return;
    }
#if 0
    sc->pmem = pci_conf_read(config_id, memreg);
    sc->vmem = pmap_mapdev(sc->pmem, 4*1024*1024);
#else
    pci_map_mem(config_id, memreg, (vm_offset_t *)(&sc->vmem), (vm_offset_t *)(&sc->pmem));
#endif
    log(LOG_INFO,"digi%d: irq %d pmem 0x%lx vmem 0x%p\n",
	(int)unit,irq,sc->pmem,sc->vmem);

    sc->port=0;
    sc->wport=0;
    sc->win_size=4*1024*1024;
    sc->mem_size=0; /* XXX */
    sc->mem_seg=0;
    sc->psize=0;
    if (!pci_map_int(config_id, (pci_inthand_t *)digiintr, (void *)unit, &tty_imask)) {
	printf("digi%d: couldn't map interrupt\n", unit);
    }
    digi_attach(unit);
}
#else /* FreeBSD 4 */
static int
digi_pci_probe(device_t dev)
{
	struct digi_softc *sc= device_get_softc(dev);
	int memreg;

	bzero(sc, sizeof(*sc));
	sc->status=NOTINIT;
	switch (pci_get_devid(dev)>>16) {
	case 0x0002:
	    device_set_desc(dev, "Digi PCI EPC/X ASIC Version Serial Adapter");
	    sc->maxport=224;
	    memreg = 0x10;
	    sc->model = PCEPC|PCI_MODEL;
	    break;
	case 0x0004:
	    device_set_desc(dev, "Digi PCI PC/Xem ASIC Version Serial Adapter");
	    sc->maxport=128;
	    memreg = 0x10;
	    sc->model = PCXEM|PCI_MODEL;
	    break;
	case 0x0005:
	    device_set_desc(dev, "Digi PCI PC/Xr ASIC Version Serial Adapter");
	    sc->maxport=8;
	    memreg = 0x10;
	    sc->model = PCXR|PCI_MODEL;
	    break;
	case 0x0006:
	    device_set_desc(dev, "Digi PCI C/X ASIC Version Serial Adapter");
	    sc->maxport=64;
	    memreg = 0x10;
	    sc->model = PCCX|PCI_MODEL;
	    break;
	case 0x000a:
	    device_set_desc(dev, "Digi PCI EPC/X PLX Version Serial Adapter");
	    sc->maxport=224;
	    memreg = 0x18;
	    sc->model = PCEPC|PCI_MODEL;
	    break;
	case 0x0009:
	    device_set_desc(dev, "Digi PCI PC/Xr PLX Version Serial Adapter");
	    sc->maxport=8;
	    memreg = 0x18;
	    sc->model = PCXR|PCI_MODEL;
	    break;
	default: 
	    sc->status=DISABLED;
	    return (ENXIO);
	}
	sc->port=0;
	sc->wport=0;
	sc->win_size=4*1024*1024;
	sc->pmem = pci_read_config(dev, memreg, 4);
	sc->mem_size=0; /* XXX */
	sc->mem_seg=0;
	sc->psize=0;
    	return 0;
}

static int
digi_pci_attach(device_t dev)
{
    struct digi_softc *sc= device_get_softc(dev);
    int irq, unit = device_get_unit(dev);

    pci_write_config(dev, 0x40, 0, 1);
    pci_write_config(dev, 0x46, 0, 1);
    pci_write_config(dev, 0x42, 1, 1);
    irq = pci_read_config(dev, PCI_INTERRUPT_REG, 4) & 0xff;
    sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->prid, 0, ~0, 5, RF_ACTIVE);
    if (!sc->port_res) return ENXIO;
    sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mrid, 0, ~0, 1, RF_ACTIVE);
    if (!sc->mem_res) return ENXIO;
    sc->pmem = rman_get_start(sc->mem_res);
    sc->psize = rman_get_end(sc->mem_res) - sc->pmem + 1;
    sc->vmem = rman_get_virtual(sc->mem_res);

    log(LOG_INFO,"digi%d: irq %d pmem 0x%lx vmem 0x%p\n",
	(int)unit,irq,sc->pmem,sc->vmem);

    return digi_attach(unit);
}

static device_method_t digi_pci_methods[] = {
        DEVMETHOD(device_probe, digi_pci_probe),
        DEVMETHOD(device_attach,        digi_pci_attach),
        { 0, 0 }
};

static driver_t digi_pci_driver = {
        driver_name,
        digi_pci_methods,
        sizeof(struct digi_softc),
};   

DRIVER_MODULE(digi, pci, digi_pci_driver, digi_devclass, 0, 0);
#endif


#endif /* NPCI > 0 */

#if __FreeBSD__ <= 3
static u_long irq_table[] = {IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ12, IRQ15, 0};
#else
static u_long irq_table[] = {3, 5, 7, 10, 11, 12, 15, 0};
#endif

static int
digi_isa_probe(dev)
#if __FreeBSD__ <= 3
    struct isa_device	*dev;
#else
    device_t	dev;
#endif
{
    int i, t;
#if __FreeBSD__ <= 3
    int unit=dev->id_unit;
    struct digi_softc *sc= &digi_softc[unit];

    bzero(sc, sizeof(*sc));
    sc->port=dev->id_iobase;
    sc->pmem=vtophys(dev->id_maddr);
    sc->mem_size=dev->id_msize;
#else
    int unit=device_get_unit(dev);
    struct digi_softc *sc= device_get_softc(dev);

    bzero(sc, sizeof(*sc));
    sc->status=NOTINIT;
    bus_get_resource(dev, SYS_RES_IOPORT, 0, &sc->port, NULL);
    bus_get_resource(dev, SYS_RES_MEMORY, 0, &sc->pmem, &sc->psize);
    sc->mem_size = sc->psize;
#endif
    
    log(LOG_INFO,"digi%d: port 0x%lx mem 0x%lx\n", unit,sc->port,sc->pmem);
    if(inb(sc->port) == 0xff) {
	sc->status=DISABLED;
	log(LOG_ERR,"digi%d: port not found\n",unit);
	goto fail;
    }
  
    outb(sc->port, FEPRST|0x2);
    for(i=0;((t=inb(sc->port)) & FEPRST) != FEPRST;i++) {
	if(i>10000) {
	    sc->status=DISABLED;
	    log(LOG_ERR,"digi%d: failed probe reset\n",unit);
	    goto fail;
	}
	DELAY(1);
    }
  
    sc->status=NOTINIT;
    if(debug&DEBUG_INIT)
	log(LOG_DEBUG,"digi%d: got probe reset after %d us\n",unit,i);
    /* try detect card */
    if(t & 0x2) { /* PC/X* (Xe,Xe VE, Xi, Xm, Xt, Xt VE) */
	if(t & 0x1) { /* bit0 always 1 -- PC/Xi */
	    sc->model=PCXI;
	    switch(t&0x30) {
	    case 0x00:
		sc->mem_size=0x10000; break;
	    case 0x10:
		sc->mem_size=0x20000; break;
	    case 0x20:
		sc->mem_size=0x40000; break;
	    case 0x30:
		sc->mem_size=0x80000; break;
	    }
	    sc->win_size=sc->mem_size;
	    sc->mem_seg=(0x100000-sc->mem_size)>>4;
	    log(LOG_INFO,"digi%d: PC/Xi %dKb\n",unit,sc->mem_size/1024);
      } else {
	outb(sc->port, FEPRST|0x2|0x1);
	t=inb(sc->port);
	if(t & 0x1) { /* bit0 r/w -- PC/Xm */
	  sc->model=PCXM;
	  sc->win_size=sc->mem_size; /* Get memory size from conf */
	  /* XXX Get memory size after BIOS start from 0xc12 */
	  sc->mem_seg=0;
	  log(LOG_INFO,"digi%d: PC/Xm\n",unit);
	} else { /* bit0 always 1 -- PC/Xe or Xe VE */
	  if(t&0x40) { 
	    sc->model=PCXEVE;
	    sc->wport=sc->port+1;
	    sc->win_size=8192;
	    sc->mem_size=65536;
	    log(LOG_INFO,"digi%d: PC/Xe VE [%02x]\n",unit, t & 0xff);
	  } else {
	    sc->model=PCXE;
	    sc->win_size=sc->mem_size=65536;
	    log(LOG_INFO,"digi%d: PC/Xe [%02x]\n",unit, t & 0xff);
	  }
	  sc->mem_seg=(0x100000-sc->mem_size)>>4;
	}
      }
    } else { /* PC/Xem, C/X, EPC/X */
	/* Only PC/Xem, C/X, EPC/X, PC/Xe VE have window mode */
      sc->wport=sc->port+1;
      sc->win_size=32768;
      sc->mem_size=0;
      sc->mem_seg=0;
      if(t&1) { /* PC/Xem or EPC/X -- try EPC/X */
	sc->model=PCEPC;
	log(LOG_INFO,"digi%d: PC/Xem or EPC/X\n",unit);
      } else {
	sc->model=PCCX;
	log(LOG_INFO,"digi%d: C/X\n",unit);
      }
    }
#if __FreeBSD__ <= 3
    return 5;			/* we need I/O space of 5 ports */
 fail:
    return 0;
#else
    return 0;
 fail:
    return ENXIO;
#endif
}

static int
#if __FreeBSD__ <= 3
digi_isa_attach(struct isa_device *isdp)
#else
digi_isa_attach(device_t dev)
#endif
{
    int unit;
    struct digi_softc *sc;
    u_long irq, *irqptr;

#if __FreeBSD__ <= 3
    unit=isdp->id_unit;
    sc= &digi_softc[unit];
#else
    unit=device_get_unit(dev);
    sc= device_get_softc(dev);
#endif
    if(sc->status!=NOTINIT) {
	log(LOG_ERR,"digi%d: try to attach a disabled or already init card\n",unit);
	goto fail;
    }
#if __FreeBSD__ <= 3
    sc->vmem=isdp->id_maddr;
#else
    sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->prid, 0, ~0, 5, RF_ACTIVE);
    if (!sc->port_res) return ENXIO;
    sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mrid, 0, ~0, sc->psize, RF_ACTIVE);
    if (!sc->mem_res) return ENXIO;
    sc->vmem = rman_get_virtual(sc->mem_res);
#endif
#if 0
    /* Already reseted. after probe */
    outb(sc->port, FEPRST|0x2);

    for(i=0; (inb(sc->port) & FEPRST) != FEPRST ; i++)
    {
	if(i>10000)
	{
	    log(LOG_ERR,"digi%d: 1st reset failed\n",unit);
	    sc->status=DISABLED;
	    hidewin(sc);
	    return 0;
	}
	DELAY(1);
    }

    if(debug&DEBUG_INIT)
      log(LOG_DEBUG,"digi%d: got reset after %d us\n",unit,i);
#endif
    /* set up interrupt and base address */
#if __FreeBSD__ > 3
    if(bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL)) irq = 0;
#else
    irq = isdp->id_irq;
#endif
    if(irq) {
	for(irqptr=irq_table; *irqptr && *irqptr!=irq; irqptr++) ;
	if(!*irqptr){
	    log(LOG_ERR,"digi%d: illegal IRQ\n",unit);
	    sc->status=DISABLED;
	    hidewin(sc);
	    return 0;
	}
	irq= irqptr - irq_table + 1;
    }
    irq|= 0x10; /* window mode for PC/Xe VE*/
    outb(sc->port+2,((u_long)sc->pmem>>8)|irq);
    outb(sc->port+3,(u_long)sc->pmem>>16);
    outb(sc->port+4,(u_long)sc->pmem>>24);
    return digi_attach(unit);
 fail:
#if __FreeBSD__ <= 3
    return 0;
#else
    /* XXX Free resources */
    return ENXIO;
#endif
}


/* Common attach */
static int
digi_attach(int unit)
{
    struct digi_softc *sc;
    int i;
    u_char *mem;
    u_long volatile *lptr;

#if __FreeBSD__ <= 3
    sc= &digi_softc[unit];
#else
    device_t dev;
    
    sc = (struct digi_softc *) devclass_get_softc(digi_devclass, unit);
    dev = devclass_get_device(digi_devclass, unit);
    if (unit >= digi_numunits) digi_numunits = unit + 1;
    sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
    if(sc->irq_res)
	bus_setup_intr(dev, sc->irq_res, INTR_TYPE_TTY, (void (*)(void *))digiintr, (void *)unit, &sc->ih);
#endif
    sc->unit = unit;
    mem=sc->vmem;

    /* detect memory size & test memory*/
    if(sc->wport) {
	for(i=0; i<sc->win_size*128; i+=sc->win_size){
	    lptr=(u_long *)(mem+setwin(sc,i));
	    *lptr = 0xA55A3CC3;
	    if(*lptr != 0xA55A3CC3){
		if(i) break;
		log(LOG_ERR,"digi%d: memory test failed [write] at %d: 0x%lx\n",unit,i,*lptr);
		sc->status=DISABLED;
		hidewin(sc);
		goto fail;;
	    }
	    *lptr = ~0xA55A3CC3;
	    if(*lptr != ~0xA55A3CC3){
		if(i) break;
		log(LOG_ERR,"digi%d: memory test failed [inverse] at %d: 0x%lx\n",unit,i,*lptr);
		sc->status=DISABLED;
		hidewin(sc);
		goto fail;
	    }
	    if(i == 0){ /* Set first word to test value */
		*lptr = 0x87654321;
	    } else { /* Test modification of first word */
		lptr=(u_long *)(mem+setwin(sc,0));
		if(*lptr != 0x87654321) break;
	    }
	}
	sc->mem_size=i;
    } else { /* Not windowed card */
	lptr=(u_long *)mem;
	*lptr = 0xA55A3CC3;
	if(*lptr != 0xA55A3CC3){
	    log(LOG_ERR,"digi%d: memory test failed [write]: %lx\n",unit,*lptr);
	    sc->status=DISABLED;
	    hidewin(sc);
	    goto fail;
	}
	*lptr = ~0xA55A3CC3;
	if(*lptr != ~0xA55A3CC3){
	    log(LOG_ERR,"digi%d: memory test failed [inverse]: %lx\n",unit,*lptr);
	    sc->status=DISABLED;
	    hidewin(sc);
	    goto fail;
	}
	lptr=(u_long *)(mem+sc->win_size-sizeof(u_long));
	*lptr = 0xA55A3CC3;
	if(*lptr != 0xA55A3CC3){
	    log(LOG_ERR,"digi%d: memory test failed [write end]: %lx\n",unit,*lptr);
	    sc->status=DISABLED;
	    hidewin(sc);
	    goto fail;
	}
	*lptr = ~0xA55A3CC3;
	if(*lptr != ~0xA55A3CC3){
	    log(LOG_ERR,"digi%d: memory test failed [inverse end]: %lx\n",unit,*lptr);
	    sc->status=DISABLED;
	    hidewin(sc);
	    goto fail;
	}
    }
    log(LOG_INFO,"digi%d: memory size %dKB\n",unit,sc->mem_size/1024);
#if __FreeBSD__ <= 3
#if defined(DEVFS)
    sc->devfs_token_ctl = devfs_add_devswf(&digi_cdevsw,
				    (unit<<16) | DOWNLOAD_MASK, DV_CHR,
				    UID_ROOT, GID_WHEEL, 0600, "ttyD%r.ctl", unit);
    sc->ttys = NULL;
#endif
#else
    make_dev(&digi_cdevsw,(unit<<16) | DOWNLOAD_MASK, 
				    UID_ROOT, GID_WHEEL, 0600, "ttyD%r.ctl", unit);
#endif
    switch(sc->model){
    case PCXE: case PCXEVE: case PCXI: case PCXM:
	sc->bitmap=DigiXi;
	break;
    default:
	sc->bitmap=DigiConc;
    }
    sc->gdata=(struct global_data *)(sc->vmem + 0xd10);
    sc->bios= NULL; sc->fep = NULL; sc->link= NULL;
    sc->ports= NULL; 
    sc->status= NOTINIT;
#if __FreeBSD__ <= 3
    return 1;
#else
    return 0;
#endif
 fail:
    sc->status= DISABLED;
#if __FreeBSD__ <= 3
    return 0;
#else
    /* XXX Free resources */
    return ENXIO;
#endif
}

static void
digidelay(void *p)
{
        wakeup(p);
}

static void
digipoll(void *p)
{
    int i;

/*    s=spltty(); */
    timeout(digipoll, NULL, (hz>=200) ? hz / 100 : 1 );
#if __FreeBSD__ <= 3
    for(i=0; i < NDIGI; i++)
	if(digi_softc[i].intr_handler) digi_softc[i].intr_handler(i);
#else
    for(i=0; i < digi_numunits; i++) {
	struct digi_softc *sc= 
		(struct digi_softc *) devclass_get_softc(digi_devclass, i);
	if(sc && sc->intr_handler) sc->intr_handler(i);
    }
#endif
/*    splx(s); */
}

static void
digi_int_test(void *ptr)
{
    struct digi_softc *sc=(struct digi_softc *)ptr;
    if(sc->intr_timestamp.tv_sec || sc->intr_timestamp.tv_usec){
	/* interrupt OK! */
	sc->intr_handler= NULL;
	return; 
    }
    log(LOG_ERR,"digi%d: interrupt not worked, use poll mode\n", sc->unit);
    sc->intr_handler= digiintr ;
    timeout(digipoll, NULL, (hz>=200) ? hz / 100 : 1 );
}

static void
digi_copy(struct digi_softc *sc, int addr, u_char *ptr, int size)
{
    int cnt, t_addr;

    if(sc->wport == 0)
	bcopy(ptr, sc->vmem + addr, size);
    else
	while(size){
	    t_addr=setwin(sc, addr);
	    cnt= (size > sc->win_size-t_addr) ? sc->win_size-t_addr : size;
	    bcopy(ptr, sc->vmem + t_addr, cnt);
	    addr+= cnt; size-= cnt; ptr+= cnt;
	}
}

static int
digiinit(int unit)
{
    struct digi_softc *sc;
    int i;
    u_char *mem;
    u_char *ptr;
    int addr;
    struct digi_p *port;
    struct board_chan *bc;

#if __FreeBSD__ <= 3
    sc=&digi_softc[unit];
#else
    sc = (struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif

    if(sc->status==DISABLED) {
	log(LOG_ERR,"digi%d: try init a disabled card\n",unit);
	return EIO;
    }
    
    if(!sc->bios && sc->model != PCXM) {
	log(LOG_ERR,"digi%d: try init w/bios code\n",unit);
	return EIO;
    }
#if 0
    if(!sc->link && sc->model >= PCCX) {
	log(LOG_ERR,"digi%d: try init w/link info\n",unit);
	return EIO;
    }
#endif
    if(!sc->fep) {
	log(LOG_ERR,"digi%d: try init w/fep code\n",unit);
	return EIO;
    }
    sc->status= NOTINIT;

    mem=sc->vmem;
    if(sc->model & PCI_MODEL) PCIPORT = FEPRST; else  outb(sc->port, FEPRST|0x2);
    for(i=0; ((sc->model & PCI_MODEL ? PCIPORT : inb(sc->port)) & FEPRST) != FEPRST ; i++)
    {
	if(i>10)
	{
	    log(LOG_ERR,"digi%d: 1st reset failed\n",unit);
/*	    hidewin(sc); */
	    return EIO;
	}
	DELAY(1000);
    }

    if(debug&DEBUG_INIT)
      log(LOG_DEBUG,"digi%d: got 1st reset after %d ms\n",unit,i);

    switch(sc->model){
    case PCXE: case PCXEVE: case PCXI: case PCCX:
      addr=setwin(sc,sc->mem_size-2048);
      bcopy(sc->bios, mem+addr, sc->s_bios);
      break;
    case PCXEM: case PCEPC: case PCXEM|PCI_MODEL: case PCEPC|PCI_MODEL: case PCCX|PCI_MODEL: case PCXR|PCI_MODEL:
	addr=setwin(sc, 0);
	*(unsigned int *)(mem+addr)  =0x0bf00401;
	*(unsigned int *)(mem+addr+4)=0;
	digi_copy(sc, 0x1000, sc->bios, sc->s_bios); 
    case PCXM: case NONE: /* PC/Xm have resident bios */
      break;
    }
    addr=setwin(sc,0xc00);
    W(mem+addr)=0;

    if(sc->model & PCI_MODEL) PCIPORT = FEPCLR; else  outb(sc->port, FEPCLR|0x2);
    for(i=0; ((sc->model & PCI_MODEL? PCIPORT: inb(sc->port)) & FEPRST)== FEPRST ; i++) {
	if(i>10) {
	    log(LOG_ERR,"digi%d: BIOS clear reset failed\n",unit);
/*	    hidewin(sc); */
	    return EIO;
	}
	DELAY(1000);
    }

    if(debug&DEBUG_INIT) log(LOG_DEBUG,"digi%d: clear reset after %d us\n",unit,i);

    for(i=0; W(mem+addr) != 'DG'; i++) {
	int timeout_flag;
	if(i> hz * 5) {
	    log(LOG_ERR,"digi%d: BIOS download failed code=0x%x must be 0x%x\n",
		    unit,W(mem+addr), *((ushort *)"GD"));
/*	    hidewin(sc); */
	    return EIO;
	}
	timeout(digidelay, &timeout_flag, 1 );
	tsleep(&timeout_flag, TTIPRI | PCATCH, "digidelay", 0);
    }

    if(debug&DEBUG_INIT) log(LOG_DEBUG,"digi%d: BIOS loaded after %dms\n",unit,i*1000/hz);
    
    if(sc->link){
      addr=setwin(sc,0xcd0);
      bcopy(sc->link, mem+addr,21);
    }

    /* load FEP/OS */

    switch(sc->model){
    case PCXE: case PCXEVE: case PCXI: case PCXM: 
	digi_copy(sc, 0x2000, sc->fep, sc->s_fep);
	if(sc->model!=PCXM){
	    ptr=mem+setwin(sc,0xc40);
	    W(ptr)=2; W(ptr+2)= (0x100000-sc->mem_size+0x2000) >> 4;
	    W(ptr+4)=0; W(ptr+6)= 0x200; W(ptr+8)=0; W(ptr+10)=0x2000;
	    outb(sc->port,FEPREQ|0x2);
	    outb(sc->port,FEPCLR|0x2);
	    for(i=0; W(ptr); i++) {
		int timeout_flag;
		if(i>hz)	{
		    log(LOG_ERR,"digi%d: FEP/OS move failed\n",unit);
		    hidewin(sc);
		    return EIO;
		}
		timeout(digidelay, &timeout_flag, 1 );
		tsleep(&timeout_flag, TTIPRI | PCATCH, "digidelay", 0);
	    }
	    if(debug&DEBUG_INIT)
		log(LOG_DEBUG,"digi%d: FEP/OS moved after %dms\n",unit,i*1000/hz);
	}
	ptr=mem+setwin(sc,0xc40);
	W(ptr)=1;
	W(ptr+2)=0x200;
	W(ptr+4)=4;
	ptr=mem+setwin(sc,FEPSTAT);
	W(ptr)=0;
	outb(sc->port,FEPREQ|0x2);	/* send interrupt to BIOS */
	outb(sc->port,FEPCLR|0x2);
	break;
    case PCXEM: case PCEPC: case PCXEM|PCI_MODEL: case PCEPC|PCI_MODEL: case PCCX|PCI_MODEL: case PCXR|PCI_MODEL:
	digi_copy(sc, 0x1000, sc->fep, sc->s_fep);
	ptr=mem+setwin(sc,FEPSTAT);
	W(ptr)=0;
	ptr=mem+setwin(sc,0xc34);
	W(ptr)=0x1004; W(ptr+2)=0xbfc0; *(ptr-4)=3;
	ptr=mem+setwin(sc,FEPSTAT);
	break;
    case PCCX:
	digi_copy(sc, 0xd000, sc->fep, sc->s_fep);
	ptr=mem+setwin(sc,0xc40);
	W(ptr)=1;
	W(ptr+2)=FEPCODE>>4;
	W(ptr+4)=4;
	ptr=mem+setwin(sc,FEPSTAT);
	W(ptr)=0;
	outb(sc->port,FEPREQ);	/* send interrupt to BIOS */
	outb(sc->port,FEPCLR);
	break;
    }
    for(i=0; W(ptr)!= 'SO'; i++) {
	int timeout_flag;
	if(i>5 * hz) {
	    log(LOG_ERR,"digi%d: FEP/OS start failed: got %04x wait %04x\n",unit, W(ptr), 'SO');
	    hidewin(sc);
	    return EIO;
	}
	timeout(digidelay, &timeout_flag, 1 );
	tsleep(&timeout_flag, TTIPRI | PCATCH, "digidelay", 0);
    }

    if(debug&DEBUG_INIT) log(LOG_DEBUG,"digi%d: FEP/OS started after %d ms\n",unit,i*1000/hz);

    W(mem+0xe04) = 2;

    switch(sc->model){
    case PCXE: case PCXEVE: case PCXI: case PCXM:
	sc->numports= W(mem+0xc22);
    default:
	sc->numports= W(mem+0xc02);
    }

    log(LOG_INFO,"digi%d: %d ports\n",unit,sc->numports);

    sc->memcmd= sc->vmem+sc->gdata->cstart;
    sc->memevent = sc->vmem+sc->gdata->istart;
    if(sc->numports == 0) {
	log(LOG_ERR,"digi%d: no ports found\n",unit);
	sc->status=NOTINIT;
	hidewin(sc);
	return EIO;
    }

    if(sc->ports) free(sc->ports, M_TTYS);
    MALLOC(sc->ports, struct digi_p *, sizeof(struct digi_p)*sc->numports,
	   M_TTYS, M_NOWAIT);

    if(sc->ports==0) {
	log(LOG_ERR,"digi%d: unable to malloc the per port structures\n",unit);
	sc->status=NOTINIT;
	hidewin(sc);
	return EIO;
    }

    bzero(sc->ports, sizeof(struct digi_p)*sc->numports);

#if __FreeBSD__ <=3
    if(sc->ttys) free(sc->ttys, M_TTYS);
    MALLOC(sc->ttys, struct tty *, sizeof(struct tty)*sc->numports,
	   M_TTYS, M_NOWAIT);

    if(sc->ttys==0)
    {
	log(LOG_ERR,"digi%d: unable to malloc the tty structures\n",unit);
	FREE(sc->ttys, M_TTYS);
	sc->status=NOTINIT;
	hidewin(sc);
	return EIO;
    }

    bzero(sc->ttys, sizeof(struct tty)*sc->numports);
#endif

    /* We should now init per-port structures */
    bc=(struct board_chan *)(mem + 0x1000);

    for(i=0; i<sc->numports; i++, bc++)
    {
	port= &sc->ports[i];
	port->pnum=i;
	port->unit=unit;
	port->status=ENABLED;
#if __FreeBSD__ <=3
	port->tp=&sc->ttys[i];
#endif
	port->bc=bc;

	switch(sc->model){
	case PCXE: case PCXEVE: case PCXI: case PCXM:
	    port->dcd= 0x08;
	    port->dsr= 0x10;
	default:
	    port->dcd= 0x80;
	    port->dsr= 0x20;
	}
	port->txbuf=mem+( ((bc->tseg-sc->mem_seg)<<4)%sc->win_size );
	port->rxbuf=mem+( ((bc->rseg-sc->mem_seg)<<4)%sc->win_size );
	port->txwin=FEPWIN | (((bc->tseg-sc->mem_seg)<<4)/sc->win_size);
	port->rxwin=FEPWIN | (((bc->rseg-sc->mem_seg)<<4)/sc->win_size);
	port->txbufsize=bc->tmax+1;
	port->rxbufsize=bc->rmax+1;

	fepcmd_w(port, STXLWATER, port->txbufsize/4, 10);
	fepcmd_w(port, SRXLWATER, port->rxbufsize/4, 10);
	fepcmd_w(port, SRXHWATER, 3*port->rxbufsize/4, 10);

	bc->edelay=100;
	port->dtr_wait= 3*hz;

	/*
	 * We don't use all the flags from <sys/ttydefaults.h> since they
	 * are only relevant for logins.  It's important to have echo off
	 * initially so that the line doesn't start blathering before the
	 * echo flag can be turned off.
	 */
	port->it_in.c_iflag = 0;
	port->it_in.c_oflag = 0;
	port->it_in.c_cflag = TTYDEF_CFLAG;
	port->it_in.c_lflag = 0;
	termioschars(&port->it_in);
	port->it_in.c_ispeed = port->it_in.c_ospeed = digidefaultrate;
	port->it_out = port->it_in;
	port->send_ring = 1; /* Default action on signal RI */
#if __FreeBSD__ <= 3
#if defined(DEVFS)
	port->devfs_token_ttyd = devfs_add_devswf(&digi_cdevsw,
		(unit<<16)+i, DV_CHR,
		UID_ROOT, GID_WHEEL, 0600, "ttyD%d.%d", unit, i);
	port->devfs_token_ttyi = devfs_add_devswf(&digi_cdevsw,
		(unit<<16)+i | CONTROL_INIT_STATE, DV_CHR,
		UID_ROOT, GID_WHEEL, 0600, "ttyiD%d.%d", unit, i);
	port->devfs_token_ttyl = devfs_add_devswf(&digi_cdevsw,
		(unit<<16)+i | CONTROL_LOCK_STATE, DV_CHR,
		UID_ROOT, GID_WHEEL, 0600, "ttylD%d.%d", unit, i);
	port->devfs_token_cuaa = devfs_add_devswf(&digi_cdevsw,
		(unit<<16)+i | CALLOUT_MASK, DV_CHR,
		UID_UUCP, GID_DIALER, 0660, "cuaD%d.%d", unit, i);
	port->devfs_token_cuai = devfs_add_devswf(&digi_cdevsw,
		(unit<<16)+i | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR,
		UID_UUCP, GID_DIALER, 0660, "cuaiD%d.%d", unit, i);
	port->devfs_token_cual = devfs_add_devswf(&digi_cdevsw,
		(unit<<16)+i | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR,
		UID_UUCP, GID_DIALER, 0660, "cualD%d.%d", unit, i);
#endif
#else
	make_dev(&digi_cdevsw, (unit<<16)+i,
		UID_ROOT, GID_WHEEL, 0600, "ttyD%d.%d", unit, i);
	make_dev(&digi_cdevsw, ((unit<<16)+i) | CONTROL_INIT_STATE,
		UID_ROOT, GID_WHEEL, 0600, "ttyiD%d.%d", unit, i);
	make_dev(&digi_cdevsw, ((unit<<16)+i) | CONTROL_LOCK_STATE,
		UID_ROOT, GID_WHEEL, 0600, "ttylD%d.%d", unit, i);
	make_dev(&digi_cdevsw, ((unit<<16)+i) | CALLOUT_MASK,
		UID_UUCP, GID_DIALER, 0660, "cuaD%d.%d", unit, i);
	make_dev(&digi_cdevsw, ((unit<<16)+i) | CALLOUT_MASK | CONTROL_INIT_STATE,
		UID_UUCP, GID_DIALER, 0660, "cuaiD%d.%d", unit, i);
	make_dev(&digi_cdevsw, ((unit<<16)+i) | CALLOUT_MASK | CONTROL_LOCK_STATE,
		UID_UUCP, GID_DIALER, 0660, "cualD%d.%d", unit, i);
#endif
    }
    sc->status=ENABLED;
    sc->opencnt++;
    timeout(digi_int_test, sc, hz);
    fepcmd_w(&sc->ports[0], 0xff, 0, 0);
    return 0;
}

#if __FreeBSD__ <= 3
struct tty *
digidevtotty(dev)
     dev_t	dev;
{
    int	mynor;
    int	unit;
    int pnum;
    struct digi_softc *sc;

    mynor = minor(dev);
    if (mynor & CONTROL_MASK)
	return (NULL);
    unit = MINOR_TO_UNIT(mynor);
    pnum=MINOR_TO_PORT(mynor);
    if ((u_int) unit >= NDIGI)
	return (NULL);
    sc=&digi_softc[unit];
    if(sc->status!=ENABLED)
    {
	log(LOG_ERR,"digi%d: try to open a disabled card\n",unit);
	return (NULL);
    }
    if(pnum>=sc->numports)
    {
	log(LOG_ERR,"digi%d: try to open non-existing port %d\n",unit,pnum);
	return (NULL);
    }
    return (sc->ports[pnum].tp);
}
#endif

static int
digimctl(port, bits, how)
    struct digi_p *port;
    int bits;
    int how;
{
    int mstat;
    Bitmap *bp;
    
    if(how == DMGET)
    {
	mstat=port->bc->mstat;
	bits = TIOCM_LE;
#if __FreeBSD__ <= 3
	for(bp=digi_softc[port->unit].bitmap;bp->from;bp++)
#else
	for(bp=((struct digi_softc *) devclass_get_softc(digi_devclass, port->unit))->bitmap;bp->from;bp++)
#endif
	    if(mstat & bp->from) bits |= bp->to;
	return bits;
    }
    mstat = 0;
    bits &= TIOCM_DTR | TIOCM_RTS;
#if __FreeBSD__ <= 3
    for(bp=digi_softc[port->unit].bitmap;bp->to;bp++)
#else
    for(bp=((struct digi_softc *) devclass_get_softc(digi_devclass, port->unit))->bitmap;bp->to;bp++)
#endif
	    if(bits & bp->to) mstat |= bp->from;
    switch (how)
    {
    case DMSET:
	fepcmd_b(port, SETMODEM, mstat, ~mstat, 0);
	break;
    case DMBIS:
	fepcmd_b(port, SETMODEM, mstat, 0, 0);
	break;
    case DMBIC:
	fepcmd_b(port, SETMODEM, 0, mstat, 0);
	break;
    }
    return 0;
}

static void
digi_disc_optim(struct tty *tp, struct termios *t, struct digi_p *port)
{
	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP))
	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
	    && (!(t->c_iflag & PARMRK)
		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
	    && linesw[tp->t_line].l_rint == ttyinput)
		tp->t_state |= TS_CAN_BYPASS_L_RINT;
	else
		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
}

int
digiopen(dev, flag, mode, p)
     dev_t		dev;
     int		flag;
     int		mode;
     struct proc	*p;
{
    struct digi_softc *sc;
    struct tty *tp;
    int unit;
    int pnum;
    struct digi_p *port;
    int s;
    int error, mynor;
    struct board_chan *bc;

    error=0;
    mynor=minor(dev);
    unit=MINOR_TO_UNIT(minor(dev));
    pnum=MINOR_TO_PORT(minor(dev));

#if __FreeBSD__ <= 3
    if(unit >= NDIGI)
    {
	log(LOG_ERR,"digi%d: try to open a nonexisting card\n",unit);
	return ENXIO;
    }

    sc=&digi_softc[unit];
#else
    if(unit >= digi_numunits)
    {
	log(LOG_ERR,"digi%d: try to open a nonexisting card\n",unit);
	return ENXIO;
    }

    sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);;
#endif

    if(mynor & DOWNLOAD_MASK) return 0;
    if(sc->status!=ENABLED)
    {
	log(LOG_ERR,"digi%d: try to open a disabled card\n",unit);
	return ENXIO;
    }

    if(pnum>=sc->numports)
    {
	log(LOG_ERR,"digi%d: try to open non-existing port %d\n",unit,pnum);
	return ENXIO;
    }

    if(mynor & CONTROL_MASK) {
      sc->opencnt++;
      return 0;
    }

    port=&sc->ports[pnum];
#if __FreeBSD__ > 3
    port->tp = dev->si_tty = ttymalloc(port->tp);
#endif
    tp=port->tp;
    bc=port->bc;

    s=spltty();

 open_top:
    while(port->status & DIGI_DTR_OFF) {
	error=tsleep(&port->dtr_wait, TTIPRI | PCATCH, "digidtr", 0);
	if (error) goto out;
    }

    if (tp->t_state & TS_ISOPEN)
    {
	/*
	 * The device is open, so everything has been initialized.
	 * Handle conflicts.
	 */
	if (mynor & CALLOUT_MASK)
	{
	    if (!port->active_out)
	    {
		error = EBUSY;
		if(debug&DEBUG_OPEN)
		  log(LOG_DEBUG,"digi%d: port %d: BUSY error=%d\n",unit,pnum,error);
		goto out;
	    }
	}
	else
	{
	    if (port->active_out)
	    {
		if (flag & O_NONBLOCK)
		{
		    error = EBUSY;
		    if(debug&DEBUG_OPEN)
		      log(LOG_DEBUG,"digi%d: port %d: BUSY error=%d\n",unit,pnum,error);
		    goto out;
		}
		error =	tsleep(&port->active_out, TTIPRI | PCATCH, "digibi", 0);
		if (error != 0)
		{
		  if(debug&DEBUG_OPEN)
		    log(LOG_DEBUG,"digi%d: port %d: tsleep(digibi) error=%d\n",
			    unit,pnum,error);
		    goto out;
		}
		goto open_top;
	    }
	}
	if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0)
	{
	    error = EBUSY;
	    goto out;
	}
    }
    else
    {
	/*
	 * The device isn't open, so there are no conflicts.
	 * Initialize it.  Initialization is done twice in many
	 * cases: to preempt sleeping callin opens if we are
	 * callout, and to complete a callin open after DCD rises.
	 */
	tp->t_oproc=digistart;
	tp->t_param=digiparam;
	tp->t_dev=dev;
#if __FreeBSD__ >= 4
	tp->t_stop = digistop;
#endif
	tp->t_termios= (mynor & CALLOUT_MASK) ? port->it_out : port->it_in;
	setwin(sc,0);
	digimctl(port, TIOCM_DTR | TIOCM_RTS, DMSET);
/*	port->imodem=bc->mstat; */
	bc->rout=bc->rin;	/* clear input queue */
	bc->idata=1; bc->iempty=1; bc->ilow=1; bc->mint=port->dcd|0x40;
	bc->tin=bc->tout;
	++port->wopeners;
	error=digiparam(tp, &tp->t_termios);
	--port->wopeners;

	if(error!=0)
	{
	  if(debug&DEBUG_OPEN)
	    log(LOG_DEBUG,"digi%d: port %d: digiparam error=%d\n",unit,pnum,error);
	  goto out;
	}

	ttsetwater(tp);

	/* handle fake DCD for callout devices */
	/* and initial DCD */

	if( (bc->mstat & port->dcd) || mynor & CALLOUT_MASK )
	    linesw[tp->t_line].l_modem(tp,1);
    }
  
    /*
     * Wait for DCD if necessary.
     */
    if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
	&& !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK))
    {
	++port->wopeners;
	error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "digidcd", 0);
	--port->wopeners;
	if (error != 0)
	{
	  if(debug&DEBUG_OPEN)
	    log(LOG_DEBUG,"digi%d: port %d: tsleep(digidcd) error=%d\n",unit,pnum,error);
	    goto out;
	}
	goto open_top;
    }
    error = linesw[tp->t_line].l_open(dev, tp);
    if(debug&DEBUG_OPEN)
      log(LOG_DEBUG,"digi%d: port %d: l_open error=%d\n",unit,pnum,error);

    digi_disc_optim(tp,&tp->t_termios,port);
  
    if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
      port->active_out = TRUE;

    if (tp->t_state & TS_ISOPEN){
      sc->opencnt++;
    }

 out:
    splx(s);

    if( !(tp->t_state & TS_ISOPEN) && port->wopeners==0 )
	digihardclose(port);

    if(debug&DEBUG_OPEN)
      log(LOG_DEBUG,"digi%d: port %d: open() returns %d\n",unit,pnum,error);

    return error;
}

/*ARGSUSED*/
int
digiclose(dev, flag, mode, p)
     dev_t		dev;
     int		flag;
     int		mode;
     struct proc	*p;
{
    int		mynor;
    struct tty	*tp;
    int unit, pnum;
    struct digi_softc *sc;
    struct digi_p *port;
    int s;

    mynor=minor(dev);
    unit=MINOR_TO_UNIT(mynor);
    pnum=MINOR_TO_PORT(mynor);

#if __FreeBSD__ <=3
    sc=&digi_softc[unit];
#else
    sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
    if(mynor & DOWNLOAD_MASK){
      return 0;
    }
    port=sc->ports+pnum;
    tp=port->tp;


    if(mynor & CONTROL_MASK){
      if( --sc->opencnt == 0)
      return 0;
    }
    
    if(debug&DEBUG_CLOSE)
      log(LOG_DEBUG,"digi%d: port %d: closing\n",unit,pnum);

    s=spltty();
    linesw[tp->t_line].l_close(tp, flag);
    digi_disc_optim(tp,&tp->t_termios,port);
    digistop(tp, FREAD | FWRITE);
    digihardclose(port);
    ttyclose(tp);
    if( --sc->opencnt == 0)
    splx(s);
    return (0);
}

static void
digidtrwakeup(void *chan)
{
    struct digi_p *port;
    port = (struct digi_p *)chan;
    port->status &= ~DIGI_DTR_OFF;
    wakeup(&port->dtr_wait);
}

static void
digihardclose(port)
     struct digi_p *port;
{
    struct digi_softc *sc;
    struct board_chan *bc;
    int s;

#if __FreeBSD__ <= 3 
    sc=&digi_softc[port->unit];
#else
    sc=(struct digi_softc *) devclass_get_softc(digi_devclass, port->unit);
#endif
    bc=port->bc;

    s=spltty();
    setwin(sc,0);
    bc->idata=0; bc->iempty=0; bc->ilow=0; bc->mint=0;
    if (port->tp->t_cflag & HUPCL
	|| (!port->active_out && !(bc->mstat & port->dcd) && !(port->it_in.c_cflag & CLOCAL))
	|| (!(port->tp->t_state & TS_ISOPEN)))
    {
	digimctl(port, TIOCM_DTR, DMBIC);
	if (port->dtr_wait != 0) {
	    timeout(&digidtrwakeup, port, port->dtr_wait);
	    port->status |= DIGI_DTR_OFF;
	}
    }
    port->active_out = FALSE;
    wakeup(&port->active_out);
    wakeup(TSA_CARR_ON(port->tp));
    splx(s);
}

int
digiread(dev, uio, flag)
     dev_t		dev;
     struct uio	*uio;
     int		flag;
{
    int		mynor;
    struct tty	*tp;
    int error, unit, pnum;

    mynor=minor(dev);
    if (mynor & CONTROL_MASK)
	return (ENODEV);
    unit=MINOR_TO_UNIT(mynor);
    pnum=MINOR_TO_PORT(mynor);

#if __FreeBSD__ <= 3 
    tp=&digi_softc[unit].ttys[pnum];
#else
    tp=((struct digi_softc *) devclass_get_softc(digi_devclass, unit))->ports[pnum].tp;
#endif


    error=linesw[tp->t_line].l_read(tp, uio, flag);
    if(debug&DEBUG_READ)
      log(LOG_DEBUG,"digi%d: port %d: read() returns %d\n",unit,pnum,error);

    return error;
}

int
digiwrite(dev, uio, flag)
     dev_t		dev;
     struct uio	*uio;
     int		flag;
{
    int		mynor;
    struct tty	*tp;
    int error, unit, pnum;

    mynor=minor(dev);
    if (mynor & CONTROL_MASK)
	return (ENODEV);

    unit=MINOR_TO_UNIT(mynor);
    pnum=MINOR_TO_PORT(mynor);

#if __FreeBSD__ <= 3
    tp=&digi_softc[unit].ttys[pnum];
#else
    tp=((struct digi_softc *) devclass_get_softc(digi_devclass, unit))->ports[pnum].tp;
#endif

    error=linesw[tp->t_line].l_write(tp, uio, flag);
    if(debug&DEBUG_WRITE)
      log(LOG_DEBUG,"digi%d: port %d: write() returns %d\n",unit,pnum,error);

    return error;
}

static int
digiioctl(dev, cmd, data, flag, p)
     dev_t		dev;
#if __FreeBSD__ == 2
     int		cmd;
#else
     u_long		cmd;
#endif
     caddr_t		data;
     int		flag;
     struct proc	*p;
{
    struct digi_softc *sc;
    int unit, pnum;
    struct digi_p *port;
    int mynor;
    struct tty *tp;
    struct board_chan *bc;
    int error;
    int s;
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
    int oldcmd;
    struct termios term;
#endif    

    mynor=minor(dev);
    unit=MINOR_TO_UNIT(mynor);
    pnum=MINOR_TO_PORT(mynor);

#if __FreeBSD__ <= 3
    if(unit >= NDIGI) return ENXIO;
    sc=&digi_softc[unit];
#else
    if(unit >= digi_numunits) return ENXIO;
    sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
    if(sc->status == DISABLED) return ENXIO;

#define DIGI_LFEP  _IO('e','F')
#define DIGI_LBIOS _IO('e','B')
#define DIGI_LCON  _IO('e','C')
#define DIGI_LINK  _IO('e','L')
#define DIGI_INIT  _IO('e','I')
#define DIGI_DEBUG _IO('e','D')
#define DIGI_RING  _IO('e','R')
    if(mynor & DOWNLOAD_MASK)
    switch (cmd)
    {
	u_char *in,**pptr;
	int *psize;
	struct con_bios *con;
	
    case DIGI_LFEP:
	pptr=&sc->fep;
	psize=&sc->s_fep;
	goto load;
    case DIGI_LCON:
	if((con = con_bios_list) == NULL){
	    con = con_bios_list = malloc(sizeof(*con),M_TTYS,M_NOWAIT);
	    bzero(con, sizeof(*con));
	} else {
	    while(con->next) con = con->next;
	    con= con->next = malloc(sizeof(*con),M_TTYS,M_NOWAIT);
	}
	bzero(con, sizeof(*con));
	
	pptr=&con->bios;
	psize=&con->size;
	goto load;
    case DIGI_LBIOS:
	pptr=&sc->bios;
	psize=&sc->s_bios;
	goto load;
    case DIGI_LINK:
	pptr=&sc->link;
	psize=&sc->s_link;
    load:
	in=(u_char *)(*(caddr_t *)data);
	error= copyin(in,psize,sizeof(*psize));
	if(error)return error;
	in+=sizeof(*psize);
	if(*pptr) free(*pptr,M_TTYS);
	*pptr=malloc(*psize,M_TTYS,M_NOWAIT);
	if(!*pptr)return ENOBUFS;
	error = copyin(in,*pptr,*psize);
	return error;
    case DIGI_INIT:
	return digiinit(unit);
    case DIGI_DEBUG:
	debug = *(int *) data;
	log(LOG_DEBUG, "digi%d: setting debug %x\n",unit, debug);
	return 0;
    }
    port=&sc->ports[pnum];
    if(! port->status & ENABLED) return ENXIO;
    tp=port->tp;
    bc=port->bc;

    if (mynor & CONTROL_MASK)
    {
	struct termios *ct;

	switch (mynor & CONTROL_MASK)
	{
	case CONTROL_INIT_STATE:
	    ct = mynor & CALLOUT_MASK ? &port->it_out : &port->it_in;
	    break;
	case CONTROL_LOCK_STATE:
	    ct = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in;
	    break;
	default:
	    return (ENODEV);	/* /dev/nodev */
	}
	switch (cmd)
	{
	case TIOCSETA:
#if __FreeBSD__ <= 3
	    error = suser(p->p_ucred, &p->p_acflag);
#else
	    error = suser(p);
#endif
	    if (error != 0)
		return (error);
	    *ct = *(struct termios *)data;
	    return (0);
	case TIOCGETA:
	    *(struct termios *)data = *ct;
	    return (0);
	case TIOCGETD:
	    *(int *)data = TTYDISC;
	    return (0);
	case TIOCGWINSZ:
	    bzero(data, sizeof(struct winsize));
	    return (0);
	default:
	    return (ENOTTY);
	}
    }
    tp = port->tp;
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
    term = tp->t_termios;
    oldcmd = cmd;
    error = ttsetcompat(tp, &cmd, data, &term);
    if(error != 0) return error;
    if(cmd != oldcmd) data = (caddr_t)&term;
#endif    
    if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF)
    {
	int	cc;
	struct termios *dt = (struct termios *)data;
	struct termios *lt = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in;

	dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag);
	dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag);
	dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag);
	dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag);
	port->c_iflag = dt->c_iflag & (IXOFF|IXON|IXANY);
	dt->c_iflag &= ~(IXOFF|IXON|IXANY);
	for (cc = 0; cc < NCCS; ++cc)
	    if (lt->c_cc[cc] != 0)
		dt->c_cc[cc] = tp->t_cc[cc];
	if (lt->c_ispeed != 0)
	    dt->c_ispeed = tp->t_ispeed;
	if (lt->c_ospeed != 0)
	    dt->c_ospeed = tp->t_ospeed;
    }

    error = linesw[tp->t_line].l_ioctl(tp, cmd, data, flag, p);
    if (error == 0 && cmd == TIOCGETA) 
	((struct termios *)data)->c_iflag |= port->c_iflag;
#if __FreeBSD__ == 2
    if (error >= 0 ) return error;
#else
    if (error >= 0 && error != ENOIOCTL) return error;
#endif
    s = spltty();
    error = ttioctl(tp, cmd, data, flag);
    if (error == 0 && cmd == TIOCGETA) 
	((struct termios *)data)->c_iflag |= port->c_iflag;
    digi_disc_optim(tp, &tp->t_termios, port);
#if __FreeBSD__ == 2
    if (error >= 0 )
#else
    if (error >= 0 && error != ENOIOCTL)
#endif
    {
	splx(s);
	return error;
    }
    setwin(sc,0);
    switch (cmd)
    {
    case DIGI_RING:
	port->send_ring = *(u_char *)data;
	break;
    case TIOCSBRK:
	/* now it sends 250 millisecond break because I don't know */
	/* how to send an infinite break */

	fepcmd_w(port, SENDBREAK, 250, 10);
	break;
    case TIOCCBRK:
	/* now it's empty */
	break;
    case TIOCSDTR:
	digimctl(port, TIOCM_DTR, DMBIS);
	break;
    case TIOCCDTR:
	digimctl(port, TIOCM_DTR, DMBIC);
	break;
    case TIOCMSET:
	digimctl(port, *(int *)data, DMSET);
	break;
    case TIOCMBIS:
	digimctl(port, *(int *)data, DMBIS);
	break;
    case TIOCMBIC:
	digimctl(port, *(int *)data, DMBIC);
	break;
    case TIOCMGET:
	*(int *)data = digimctl(port, 0, DMGET);
	break;
    case TIOCMSDTRWAIT:
#if __FreeBSD__ <= 3
	error = suser(p->p_ucred, &p->p_acflag);
#else
	error = suser(p);
#endif
	if (error != 0) {
	    splx(s);
	    return error;
	}
	port->dtr_wait = *(int *)data * hz / 100;
	break;
    case TIOCMGDTRWAIT:
	*(int *)data = port->dtr_wait * 100 / hz;
	break;
    case TIOCTIMESTAMP:
	*(struct timeval *)data = sc->intr_timestamp;
	break;
    default:
	splx(s);
	return ENOTTY;
    }
    splx(s);
    return 0;
}

static int
digiparam(tp, t)
     struct tty	*tp;
     struct termios	*t;
{
    int mynor;
    int unit;
    int pnum;
    struct digi_softc *sc;
    struct digi_p *port;
    struct board_chan *bc;
    int cflag;
    int iflag;
    int hflow;
    int s;

    mynor=minor(tp->t_dev);
    unit=MINOR_TO_UNIT(mynor);
    pnum=MINOR_TO_PORT(mynor);
#if __FreeBSD__ <= 3
    sc=&digi_softc[unit];
#else
    sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
    port=&sc->ports[pnum];
    bc=port->bc;

    if(debug&DEBUG_SET)
      log(LOG_DEBUG,"digi%d: port%d: setting parameters\n",unit,pnum);

    if (t->c_ispeed == 0)
	t->c_ispeed = t->c_ospeed;

    cflag=ttspeedtab(t->c_ospeed, digispeedtab);

    if (cflag < 0 || (cflag > 0 && t->c_ispeed != t->c_ospeed))
	return (EINVAL);

    s=spltty();

    setwin(sc,0);

    if(cflag==0)
    {				/* hangup */
      if(debug&DEBUG_SET)
	log(LOG_DEBUG,"digi%d: port%d: hangup\n",unit,pnum);
	digimctl(port, TIOCM_DTR, DMBIC);
    }
    else
	digimctl(port, TIOCM_DTR, DMBIS);
    
    if(debug&DEBUG_SET)
      log(LOG_DEBUG,"digi%d: port%d: CBAUD=%d\n",unit,pnum,cflag);

    /* convert flags to sysV-style values */
    if(t->c_cflag & PARODD) cflag|=0x0200;
    if(t->c_cflag & PARENB) cflag|=0x0100;
    if(t->c_cflag & CSTOPB) cflag|=0x0080;

    cflag|= (t->c_cflag & CSIZE) >> 4;
    if(debug&DEBUG_SET)
      log(LOG_DEBUG,"digi%d: port%d: CFLAG=0x%x\n",unit,pnum,cflag);
    fepcmd_w(port, SETCFLAGS, (unsigned)cflag, 0);
    iflag=t->c_iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP);
    if(port->c_iflag & IXON)	iflag|= 0x400;
    if(port->c_iflag & IXANY)	iflag|= 0x800;
    if(port->c_iflag & IXOFF)	iflag|=0x1000;

    if(debug&DEBUG_SET)
      log(LOG_DEBUG,"digi%d: port%d: set iflag=0x%x\n",unit,pnum,iflag);
    fepcmd_w(port, SETIFLAGS, (unsigned)iflag, 0);
    /*  bc->mint=port->dcd; */

    hflow = 0;
    switch(sc->model){
    case PCXE: case PCXEVE: case PCXI: case PCXM:
	if(t->c_cflag & CDTR_IFLOW)	hflow|= 0x80;
	if(t->c_cflag & CRTS_IFLOW)	hflow|= 0x02;
	if(t->c_cflag & CCTS_OFLOW)	hflow|= 0x20;
	if(t->c_cflag & CDSR_OFLOW)	hflow|= 0x10;
	if(t->c_cflag & CCAR_OFLOW)	hflow|= 0x08;
    default:
	if(t->c_cflag & CDTR_IFLOW)	hflow|= 0x01;
	if(t->c_cflag & CRTS_IFLOW)	hflow|= 0x02;
	if(t->c_cflag & CCTS_OFLOW)	hflow|= 0x10;
	if(t->c_cflag & CDSR_OFLOW)	hflow|= 0x20;
	if(t->c_cflag & CCAR_OFLOW)	hflow|= 0x80;
    }

    if(debug&DEBUG_SET)
      log(LOG_DEBUG,"digi%d: port%d: set hflow=0x%x\n",unit,pnum,hflow);
    fepcmd_w(port, SETHFLOW, 0xff00|(unsigned)hflow, 0);

    if(debug&DEBUG_SET)
      log(LOG_DEBUG,"digi%d: port%d: set startc(0x%x), stopc(0x%x)\n",
	    unit,pnum, t->c_cc[VSTART], t->c_cc[VSTOP]);
    fepcmd_b(port, SONOFFC, t->c_cc[VSTART], t->c_cc[VSTOP], 0);
    splx(s);

    return 0;

}

void
digiintr(unit)
     int	unit;
{
    struct digi_p *port;
    char *cxcon;
    struct digi_softc *sc;
    int ehead, etail;
    struct board_chan *bc;
    struct tty *tp;
    int head, tail;
    int wrapmask;
    int size,window;
    struct event
    {
	u_char pnum,event,mstat,lstat;
    } event;

#ifdef COM_LOCK
    COM_LOCK();
#endif
#if __FreeBSD__ <=3
    sc=&digi_softc[unit];
#else
    sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
    if(sc->status!=ENABLED)
    {
      if(debug&DEBUG_IRQ)
	log(LOG_DEBUG,"digi%d: interrupt of not enabled board!\n",unit);
	return;
    }
    microtime(&sc->intr_timestamp);
    if(sc->wport){ window=sc->window; setwin(sc,0); }

    if(W(sc->vmem+0xd00))
    {
	struct con_bios *con=con_bios_list;
	register u_char *ptr;

	ptr=sc->vmem+W(sc->vmem+0xd00);
	while(con){
	    if(ptr[1] && W(ptr+2) == W(con->bios+2))  /* Not first block -- exact match */
		break;
	    if(W(ptr+4) >= W(con->bios+4) && W(ptr+4) <= W(con->bios+6))
		break; /* Initial search concetrator BIOS */
	}
	if(con == NULL)
	{
	    log(LOG_ERR,"digi%d: wanted bios LREV=0x%04x not found!\n",unit,W(ptr+4));
	    W(ptr+10)=0;
	W(sc->vmem+0xd00)=0;
	    goto eoi;
	}
	cxcon=con->bios;
	W(ptr+4)=W(cxcon+4);
	W(ptr+6)=W(cxcon+6);
	if(ptr[1]==0) W(ptr+2)=W(cxcon+2);
	W(ptr+8)=(ptr[1]<<6)+W(cxcon+8);
 	size=W(cxcon+10)-(ptr[1]<<10);
	if(size<=0)
	{
	    W(ptr+8)=W(cxcon+8);
	    W(ptr+10)=0;
	}
	else
	{
	    if(size>1024) size=1024;
	    W(ptr+10)=size;
	    bcopy(cxcon+(ptr[1]<<10),ptr+12,size);
	}
	W(sc->vmem+0xd00)=0;
	goto eoi;
    }
    ehead=sc->gdata->ein;
    etail=sc->gdata->eout;
    if(ehead==etail)
    {
      if(debug&DEBUG_IRQ && sc->intr_handler == NULL)
      	log(LOG_DEBUG,"digi%d: DUMMY interrupt %x %x\n",unit,ehead,etail);
	goto eoi;
    }
    while(ehead!=etail)
    {
	event= *(struct event *)&sc->memevent[etail];
	if(debug&DEBUG_IRQ) log(LOG_DEBUG,"digi%d: EVENT %04x:  %02x port %d [%02x/%02x]\n", unit,etail,event.event, event.pnum, event.mstat,event.lstat);
	etail= (etail+4) & sc->gdata->imax;
      
	if(event.pnum>=sc->numports)
	{
	    log(LOG_ERR,"digi%d: port %d: got event on nonexisting port\n",
		   unit,event.pnum);
	    continue;
	}
	
	port=&sc->ports[event.pnum];
	bc=port->bc;
	tp=port->tp;
	
	if( !(tp->t_state & TS_ISOPEN) )
	{
	  if(debug&DEBUG_IRQ)
	    log(LOG_DEBUG,"digi%d: port %d: got event 0x%x on closed port\n",
		    unit,event.pnum,event.event);
	    bc->rout=bc->rin;
	    bc->idata=0; bc->iempty=0; bc->ilow=0; bc->mint=0;
	    continue;
	}
	if( event.event & ~ALL_IND )
	    log(LOG_ERR,"digi%d: port%d: ? event 0x%x mstat 0x%x lstat 0x%x\n",
		   unit, event.pnum, event.event, event.mstat, event.lstat);

	if(event.event & DATA_IND)
	{
	  if(debug&DEBUG_IRQ)
	    log(LOG_DEBUG,"digi%d: port %d: DATA_IND\n",unit,event.pnum);
	    wrapmask=port->rxbufsize-1;
	    head=bc->rin;
	    tail=bc->rout;

	    size=0;
	    if( !(tp->t_state & TS_ISOPEN) )
	    {
		bc->rout=head;
		goto end_of_data;
	    }

	    while(head!=tail)
	    {
		int top;
	      
		if(debug&DEBUG_INT)
		  log(LOG_DEBUG,"digi%d: port %d: p rx head=%d tail=%d\n",
			unit,event.pnum,head,tail);
		top= (head>tail)?head:wrapmask+1;
		towin(sc,port->rxwin);
		size=top-tail;
		if(tp->t_state & TS_CAN_BYPASS_L_RINT)
		{
		    size =
			b_to_q((char *)port->rxbuf+tail,size,&tp->t_rawq);
/*		    port->delta_error_counts[CE_TTY_BUF_OVERFLOW]+= size; */
		    tail = top-size;
		    ttwakeup(tp);
		}
		else
		{
		    for(;tail<top;) {
			linesw[tp->t_line].l_rint(port->rxbuf[tail],tp);
			towin(sc,port->rxwin);
			size--; tail++;
			if(tp->t_state & TS_TBLOCK) break;
		    }
		}
		tail &= wrapmask;
		setwin(sc,0);
		bc->rout=tail;
		head=bc->rin;
		if(size) break; 
	    }

	    if(bc->orun)
	    {
		CE_RECORD(port, CE_OVERRUN);
		log(LOG_ERR,"digi%d: port%d: %s\n",
		       unit, event.pnum, error_desc[CE_OVERRUN]);
		bc->orun=0;
	    }

	end_of_data:
	    if(size){
		tp->t_state |= TS_TBLOCK;
		port->status |= PAUSE_RX;
		if(debug&DEBUG_RX)
			log(LOG_DEBUG,"digi%d: port %d: pause RX\n",unit,event.pnum);
		   /* fepcmd_w(port,PAUSERX,0,10); */
	    } else {
	    	bc->idata=1;
	    }
	}

	if(event.event & MODEMCHG_IND)
	{
	  if(debug&DEBUG_MODEM)
	      log(LOG_DEBUG,"digi%d: port %d: MODEMCHG_IND\n",unit,event.pnum);
	  if((event.mstat^event.lstat)&port->dcd)
	      linesw[tp->t_line].l_modem(tp,event.mstat & port->dcd);
	  if(event.mstat&0x40)
	  {
	      if(debug&DEBUG_RI)
		  log(LOG_DEBUG,"digi%d: port %d: RING\n",unit,event.pnum);
	      if(port->send_ring){
		  linesw[tp->t_line].l_rint('R',tp);
		  linesw[tp->t_line].l_rint('I',tp);
		  linesw[tp->t_line].l_rint('N',tp);
		  linesw[tp->t_line].l_rint('G',tp);
		  linesw[tp->t_line].l_rint('\r',tp);
		  linesw[tp->t_line].l_rint('\n',tp);
	      }
	  }
	}
	if(event.event & BREAK_IND)
	{
	  if(debug&DEBUG_MODEM)
	    log(LOG_DEBUG,"digi%d: port %d: BREAK_IND\n",unit,event.pnum);
	    linesw[tp->t_line].l_rint(TTY_BI, tp);
	}

	if(event.event & (LOWTX_IND | EMPTYTX_IND) )
	{
	  if(debug&DEBUG_IRQ)
	    log(LOG_DEBUG,"digi%d: port %d:%s%s\n",unit,event.pnum,
		    event.event & LOWTX_IND ? " LOWTX":"",
		    event.event & EMPTYTX_IND ? " EMPTYTX":"");
	    (*linesw[tp->t_line].l_start)(tp);
	}
    }
    sc->gdata->eout=etail;
 eoi:
    if(sc->wport){ towin(sc,window); }
#ifdef COM_LOCK
    COM_UNLOCK();
#endif
}

static void
digistart(tp)
     struct tty	*tp;
{
    int unit;
    int pnum;
    struct digi_p *port;
    struct digi_softc *sc;
    struct board_chan *bc;
    int head, tail;
    int size, ocount, totcnt=0;
    int s;
    int wmask;

    unit=MINOR_TO_UNIT(minor(tp->t_dev));
    pnum=MINOR_TO_PORT(minor(tp->t_dev));
#if __FreeBSD__ <=3
    sc=&digi_softc[unit];
#else
    sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
    port=&sc->ports[pnum];
    bc=port->bc;

    wmask=port->txbufsize-1;

    s=spltty();
    port->lcc=tp->t_outq.c_cc;
    setwin(sc,0);
    if(!(tp->t_state&TS_TBLOCK))
    {
	if(port->status&PAUSE_RX)
	if(debug&DEBUG_RX)
		log(LOG_DEBUG,"digi%d: port %d: resume RX\n",unit,pnum);
	port->status &= ~PAUSE_RX;
	/* fepcmd_w(port, RESUMERX, 0, 10); */
	bc->idata= 1;
    }
    if(!(tp->t_state&TS_TTSTOP))
    {
	if(port->status & PAUSE_TX) {
	if(debug&DEBUG_TX)
		log(LOG_DEBUG,"digi%d: port %d: resume TX\n",unit,pnum);
	port->status &= ~PAUSE_TX;
	fepcmd_w(port, RESUMETX, 0, 10);
	}
    }
    if(tp->t_outq.c_cc == 0)
    {
	tp->t_state &= ~TS_BUSY;
/*	printf("digi%d: port%d: (s) output queue empty\n",
	       unit,pnum); */
    }
    else
	tp->t_state |= TS_BUSY;

    head=bc->tin;
    while( tp->t_outq.c_cc!=0 )
    {
	tail=bc->tout;
	if(debug&DEBUG_INT)
	  log(LOG_DEBUG,"digi%d: port%d: s tx head=%d tail=%d\n",
		unit,pnum,head,tail);

	if(head<tail)
	    size=tail-head-1;
	else
	{
	    size=port->txbufsize-head;
	    if(tail==0) size--;
	}

	if(size==0) break;
	towin(sc,port->txwin);
	totcnt+=ocount=q_to_b(&tp->t_outq, port->txbuf+head, size);
	head+=ocount;
	head&=wmask;
	setwin(sc,0);
	bc->tin=head;
	bc->iempty=1; bc->ilow=1;
    }
    port->lostcc=tp->t_outq.c_cc;
    tail=bc->tout;
    if(head<tail)
	size=port->txbufsize-tail+head;
    else
    {
	size=head-tail;
    }
    port->lbuf=size;
    if(debug&DEBUG_INT)
      log(LOG_DEBUG,"digi%d: port%d: s total cnt=%d\n",unit,pnum,totcnt);
    ttwwakeup(tp);
    splx(s);
}

void
digistop(tp, rw)
     struct tty	*tp;
     int		rw;
{
    int unit;
    int pnum;
    struct digi_p *port;

    unit=MINOR_TO_UNIT(minor(tp->t_dev));
    pnum=MINOR_TO_PORT(minor(tp->t_dev));
#if __FreeBSD__ <=3
    port=&digi_softc[unit].ports[pnum];
#else
    port=&((struct digi_softc *) devclass_get_softc(digi_devclass, unit))->ports[pnum];
#endif

    if(debug&DEBUG_TX)
	log(LOG_DEBUG,"digi%d: port %d: pause TX\n",unit,pnum);
    port->status |= PAUSE_TX;
    fepcmd_w(port, PAUSETX, 0, 10);
}

static void
fepcmd(port, cmd, op1, ncmds)
     struct digi_p *port;
     int cmd, op1, ncmds;
{
#if __FreeBSD__ <=3
  struct digi_softc *sc=&digi_softc[port->unit];
#else
  struct digi_softc *sc=(struct digi_softc *) devclass_get_softc(digi_devclass, port->unit);
#endif
  u_char *mem=sc->memcmd;
  unsigned tail, head;
  int count, n;

  setwin(sc,0);
  head=sc->gdata->cin;
  mem[head+0]=cmd;
  mem[head+1]=port->pnum;
  *(ushort *)(mem+head+2)=op1;

  head=(head+4) & sc->gdata->cmax;
  sc->gdata->cin=head;

  for(count=FEPTIMEOUT; count>0; count--)
    {
      head=sc->gdata->cin;
      tail=sc->gdata->cout;
      n=(head-tail) & sc->gdata->cmax;

      if(n <= ncmds * 4)
	return;
    }

  log(LOG_ERR,"digi%d(%d): timeout on FEP command\n",
	 port->unit, port->pnum);
}
 


 




Copyright © Lexa Software, 1996-2009.