static char     nic39_id[] = "@(#)$Id: nic3009.c,v 1.13 1995/12/08 23:19:30 phk Exp $";
/*******************************************************************************
 *  II - Version 0.1 $Revision: 1.13 $   $State: Exp $
 *
 * Copyright 1994 Dietmar Friede
 *******************************************************************************
 * Bug reports, patches, comments, suggestions should be sent to:
 *
 *	jkr@saarlink.de or jkrause@guug.de
 *
 *******************************************************************************
 * $Log: nic3009.c,v $
 * Revision 1.13  1995/12/08  23:19:30  phk
 * Julian forgot to make the *devsw structures static.
 *
 * Revision 1.12  1995/12/08  11:12:47  julian
 * Pass 3 of the great devsw changes
 * most devsw referenced functions are now static, as they are
 * in the same file as their devsw structure. I've also added DEVFS
 * support for nearly every device in the system, however
 * many of the devices have 'incorrect' names under DEVFS
 * because I couldn't quickly work out the correct naming conventions.
 * (but devfs won't be coming on line for a month or so anyhow so that doesn't
 * matter)
 *
 * If you "OWN" a device which would normally have an entry in /dev
 * then search for the devfs_add_devsw() entries and munge to make them right..
 * check out similar devices to see what I might have done in them in you
 * can't see what's going on..
 * for a laugh compare conf.c conf.h defore and after... :)
 * I have not doen DEVFS entries for any DISKSLICE devices yet as that will be
 * a much more complicated job.. (pass 5 :)
 *
 * pass 4 will be to make the devsw tables of type (cdevsw * )
 * rather than (cdevsw)
 * seems to work here..
 * complaints to the usual places.. :)
 *
 * Revision 1.11  1995/11/29  14:39:08  julian
 * If you're going to mechanically replicate something in 50 files
 * it's best to not have a (compiles cleanly) typo in it! (sigh)
 *
 * Revision 1.10  1995/11/29  10:47:05  julian
 * OK, that's it..
 * That's EVERY SINGLE driver that has an entry in conf.c..
 * my next trick will be to define cdevsw[] and bdevsw[]
 * as empty arrays and remove all those DAMNED defines as well..
 *
 * Revision 1.9  1995/11/21  14:56:02  bde
 * Completed function declarations, added prototypes and removed redundant
 * declarations.
 *
 * Revision 1.8  1995/09/19  18:54:42  bde
 * Fix benign type mismatches in isa interrupt handlers.  Many returned int
 * instead of void.
 *
 * Revision 1.7  1995/09/08  11:06:47  bde
 * Fix benign type mismatches in devsw functions.  82 out of 299 devsw
 * functions were wrong.
 *
 * Revision 1.6  1995/05/11  19:25:56  rgrimes
 * Fix -Wformat warnings from LINT kernel.
 *
 * Revision 1.5  1995/03/28  07:54:33  bde
 * Add and move declarations to fix all of the warnings from `gcc -Wimplicit'
 * (except in netccitt, netiso and netns) that I didn't notice when I fixed
 * "all" such warnings before.
 *
 * Revision 1.4  1995/02/16  08:06:21  jkh
 * Fix a few bogons introduced when config lost the 3 char limitation.
 *
 * Revision 1.3  1995/02/15  11:59:41  jkh
 * Fix a few more nits.  Should compile better now! :_)
 *
 * Revision 1.2  1995/02/15  06:28:20  jkh
 * Fix up include paths, nuke some warnings.
 *
 * Revision 1.1  1995/02/14  15:00:14  jkh
 * An ISDN driver that supports the EDSS1 and the 1TR6 ISDN interfaces.
 * EDSS1 is the "Euro-ISDN", 1TR6 is the soon obsolete german ISDN Interface.
 * Obtained from: Dietmar Friede <dfriede@drnhh.neuhaus.de> and
 * 	Juergen Krause <jkr@saarlink.de>
 *
 * This is only one part - the rest to follow in a couple of hours.
 * This part is a benign import, since it doesn't affect anything else.
 *
 *
 ******************************************************************************/

/*
 * Copyright (c) 1994 Dietmar Friede (dietmar@friede.de) All rights reserved.
 * FSF/FSAG GNU Copyright applies
 *
 * A low level driver for the NICCY-3009 ISDN Card.
 *
 */

#include "nnic.h"
#if NNNIC > 0

#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/proc.h>
#ifdef DEVFS
#include <sys/devfsext.h>
#endif /*DEVFS*/

#include <i386/isa/isa_device.h>
#include <gnu/i386/isa/nic3009.h>
#include <gnu/i386/isa/niccyreg.h>
#include <gnu/isdn/isdn_ioctl.h>


#define OPEN		1
#define LOAD_HEAD	3
#define LOAD_DATA	5
#define LOAD_ENTITY     8
#define IS_DIAL(p)	(((p)&0x20)==0)
#define IS_LISTEN(p)	((p)&0x20)
#define	CHAN(pl)	(((pl)&7)-1)
#define	C_CHAN(x)	((x)&1)
#define APPL(pl)	((((pl)>>6)&0x7f)-1)
#define CARD(pl)	(((pl)>>13)&7)
#define MK_APPL(pl)	(((pl)+1)<<6)

#define min(a,b)	((a)<(b)?(a):(b))

extern isdn_appl_t isdn_appl[];
extern u_short isdn_state;
extern isdn_ctrl_t isdn_ctrl[];
extern int     ispy_applnr;
extern int Isdn_Appl, Isdn_Ctrl, Isdn_Typ;

static old_spy= 0;

static int	nnicattach __P((struct isa_device *is));
static int	nnicprobe __P((struct isa_device *is));
static int	nnic_accept __P((int cn, int an, int rea));
static int	nnic_connect __P((int cn, int ap, int b_channel, int inf_mask,
				  int out_serv, int out_serv_add,
				  int src_subadr, unsigned ad_len,
				  char *dest_addr, int spv));
static int	nnic_disconnect __P((int cn, int rea));
static int	nnic_listen __P((int cn, int ap, int inf_mask,
				 int subadr_mask, int si_mask, int spv));
static int	nnic_output __P((int cn));
static int	nnic_state __P((int cn));

static	d_open_t	nnicopen;
static	d_close_t	nnicclose;
static	d_ioctl_t	nnicioctl;

#define CDEV_MAJOR 60
static struct cdevsw nnic_cdevsw = 
	{ nnicopen,	nnicclose,	noread,		nowrite,	/*60*/
	  nnicioctl,	nostop,		nullreset,	nodevtotty,/* nnic */
	  seltrue,	nommap,		NULL,	"nnic",	NULL,	-1 };


static short    bsintr;

struct isa_driver nnicdriver = {nnicprobe, nnicattach, "nnic"};

typedef enum
{
	DISCON, ISDISCON, DIAL, CALLED, CONNECT, IDLE, ACTIVE
}               io_state;

typedef struct
{
	char            ctrl;
	u_char          msg_nr;
	u_char		morenr;
	short           plci;
	short           ncci;
	short           state;
	short           i_len;
	char            i_buf[2048];
	char            o_buf[2048];
	u_short         more;
	char           *more_b;
}               chan_t;

static struct nnic_softc
{
	dpr_type       *sc_dpr;	/* card RAM virtual memory base */
	u_short         sc_vector;	/* interrupt vector 		 */
	short           sc_port;
	u_char          sc_flags;
	u_char          sc_unit;
	u_char          sc_ctrl;
	u_char          sc_type;
	short           sc_stat;
	chan_t          sc_chan[2];
#ifdef	DEVFS
	void		*devfs_token;
#endif
}               nnic_sc[NNNIC];

static void	badstate __P((mbx_type *mbx, int n, int mb, dpr_type *dpr));
static int	con_b3_resp __P((int unit, int mb, u_short ncci, u_short pl,
				 u_char reject));
static int	discon_req __P((int w, int unit, int pl, int rea, int err));
static int	con_resp __P((int unit, int pl, int rea));
static void	dn_intr __P((unsigned unit, struct nnic_softc *sc));
static int	en_q __P((int unit, int t, int st, int pl, int l, u_char *b));
static void	make_intr __P((void *gen));
static void	nnnicintr __P((void *gen));
static void	nnic_reset __P((struct nnic_softc *sc, int reset));
static int	reset_plci __P((int w, chan_t *chan, int p));
static int	sel_b2_prot_req __P((int unit, int c, int pl, dlpd_t *dlpd));
static int	sel_b3_prot_req __P((int unit, int mb, u_short pl,
				     ncpd_t *ncpd));
static void	up_intr __P((unsigned unit, struct nnic_softc *sc));

static int
nnicprobe(struct isa_device * is)
{
	register struct nnic_softc *sc = &nnic_sc[is->id_unit & 127];
	dpr_type       *dpr;
	u_char *w;
	int i;

	sc->sc_vector = is->id_irq;
	sc->sc_port = is->id_iobase;
	sc->sc_unit = is->id_unit;
	w= (u_char *) dpr = sc->sc_dpr = (dpr_type *) is->id_maddr;

	i= ffs(sc->sc_vector)-1;
	if(i == 9) i= 1;
	outb(sc->sc_port, 0);
	outb(sc->sc_port+1, i);
	outb(sc->sc_port, ((unsigned) dpr >> 12) & 0xff);

/* There should be memory, so lets test that */
	for (i=0;i<DPR_LEN;i++)
		w[i] = (i+0xaf) & 0xff;
	for (i=0;i<DPR_LEN;i++)
		if (w[i] != ((i+0xaf) & 0xff))
		{
			printf("Niccy card not found or bad memory %p\n",
				is->id_maddr);
			outb(sc->sc_port, 0);
			return(0);
		}
	bzero(w,DPR_LEN-4);

	is->id_msize = DPR_LEN;
	return (2);
}

static void
nnic_reset(struct nnic_softc *sc, int reset)
{
	u_char o;
	o= ffs(sc->sc_vector)-1;
	if(reset == 0)
		o|= 0x80;
	outb(sc->sc_port+1,o);
}

/*
 * nnicattach() Install device
 */
static int
nnicattach(struct isa_device * is)
{
	struct nnic_softc *sc;
	int             cn;
	isdn_ctrl_t    *ctrl0, *ctrl1;
	char		name[32];

	sc = &nnic_sc[is->id_unit];
	sc->sc_ctrl = -1;
	if ((cn = isdn_ctrl_attach(2)) == -1)
	{
		return (0);
	}
	sc->sc_ctrl = cn;
	sc->sc_chan[0].plci = sc->sc_chan[1].plci = -1;

	ctrl0 = &isdn_ctrl[cn];
	ctrl1 = &isdn_ctrl[cn + 1];
	sc->sc_chan[0].ctrl = ctrl0->ctrl = cn;
	sc->sc_chan[1].ctrl = ctrl1->ctrl = cn + 1;
	ctrl0->o_buf = sc->sc_chan[0].o_buf;
	ctrl1->o_buf = sc->sc_chan[1].o_buf;
	ctrl0->listen = ctrl1->listen = nnic_listen;
	ctrl0->disconnect = ctrl1->disconnect = nnic_disconnect;
	ctrl0->accept = ctrl1->accept = nnic_accept;
	ctrl0->connect = ctrl1->connect = nnic_connect;
	ctrl0->output = ctrl1->output = nnic_output;
	ctrl0->state = ctrl1->state = nnic_state;
	ctrl0->unit = ctrl1->unit = is->id_unit;
	ctrl0->appl = ctrl1->appl = -1;
	ctrl0->o_len = ctrl1->o_len = -1;
	sc->sc_flags= LOAD_ENTITY;
#ifdef	DEVFS
	sprintf(name,"nnic%d",is->id_unit);
	sc->devfs_token = devfs_add_devsw("/isdn",name,
		&nnic_cdevsw, is->id_unit, DV_CHR, 0, 0, 0600 );
#endif

	return (1);
}

#define con_b3_req(unit,mb,pl)	en_q(unit,mb|BD_CONN_B3_REQ,0,pl,0,NULL)
#define con_act_resp(unit,pl)	en_q(unit,DD_CONN_ACT_RSP,0, pl,0,NULL)
#define discon_resp(sc,pl)	en_q(unit,DD_DISC_RSP,0, pl,0,NULL)
#define inf_resp(unit,pl)	en_q(unit,DD_INFO_RSP,0, pl,0,NULL)
#define listen_b3_req(unit,mb,pl) en_q(unit,mb|BD_LIST_B3_REQ,0,pl,0,NULL)

/* If the Niccy card wants it: Interupt it. */
static void
make_intr(void *gen)
{
	struct nnic_softc * sc = (struct nnic_softc *)gen;
	dpr_type       *dpr = sc->sc_dpr;

	dpr->watchdog_cnt = 0xFF;

	if (dpr->int_flg_nic)
		dpr->signal_pc_to_niccy++;
}

static int
en_q(int unit, int t, int st, int pl, int l, u_char *b)
{
	struct nnic_softc * sc= &nnic_sc[unit];
	dpr_type       *dpr = sc->sc_dpr;
	mbx_type       *mbx = &dpr->dn_mbx;

/*
	if (dpr->card_state & ~4)
		return (ENODEV);
*/
	if (mbx->msg_flag)
		return (EBUSY);

	bzero(&mbx->type, 18);
	mbx->type = t;
	mbx->subtype = st;
	mbx->plci = pl;
	if (l)
	{
		mbx->data_len = l;
		bcopy(b, mbx->data, l);
	}
	mbx->msg_flag = 1;
	make_intr(sc);
	return (0);
}

static void
badstate(mbx_type * mbx, int n, int mb, dpr_type *dpr)
{
	printf("Niccy: not implemented %x len %d at %d.", mbx->type,mbx->data_len,n);
	if(mbx->data_len)
	{
		int i;

		for(i=0; i<mbx->data_len; i++) printf(" %x",mbx->data[i]);
	}
	printf("\n");
}

static int
nnic_state(int cn)
{
	isdn_ctrl_t    *ctrl = &isdn_ctrl[cn];
	struct nnic_softc *sc = &nnic_sc[ctrl->unit];
	chan_t         *chan0 = &sc->sc_chan[0];
	chan_t         *chan1 = &sc->sc_chan[1];
	dpr_type       *dpr = sc->sc_dpr;

	if(sc->sc_flags == LOAD_ENTITY)
		return(ENODEV);
	if (dpr->card_state & ~4 )
		return (ENODEV);
	if (dpr->card_state & 4 )
		return (EAGAIN);
	if(chan0->state && chan1->state)
		return(EBUSY);
	return(0);
}

static int
nnic_output(int cn)
{
	isdn_ctrl_t    *ctrl = &isdn_ctrl[cn];
	struct nnic_softc *sc = &nnic_sc[ctrl->unit];
	chan_t         *chan = &sc->sc_chan[C_CHAN(cn)];
	dpr_type       *dpr = sc->sc_dpr;
	mbx_type       *mbx = &dpr->dn_mbx;
	int             l;
	int		len= ctrl->o_len;

	if (dpr->card_state /* & ~4 */)
		return (ENODEV);

	if (bsintr || (chan->ncci == -1) || mbx->msg_flag || (chan->state != IDLE))
		return (EBUSY);

	chan->state = ACTIVE;

	bzero(&mbx->type, 20);
	mbx->type = BD_DATA_B3_REQ;
	if (C_CHAN(cn))
		mbx->type |= 0x40;
	*(u_short *) mbx->data = chan->ncci;
	mbx->data[4] = chan->msg_nr;
	l = min(DATAFIELD_LEN-5, len);
	mbx->data_len = l+5;
	bcopy(ctrl->o_buf, &mbx->data[5], l);

	if (l < len)
	{
		chan->more = len - l;
		chan->more_b = ctrl->o_buf + l;
		mbx->more_data = 1;
		bsintr = C_CHAN(cn)+1;
	} else
	{
		chan->more = 0;
		ctrl->o_len = -1;
		bsintr= 0;
		++chan->msg_nr;
	}

	mbx->msg_flag = 1;
	make_intr(sc);
	ctrl->lastact = time.tv_sec;
	return (0);
}

static int
con_resp(int unit, int pl, int rea)
{
	return(en_q(unit, DD_CONN_RSP, 0, pl, 1, (u_char *) & rea));
}

static int
reset_plci(int w, chan_t * chan, int p)
{
	isdn_ctrl_t    *ctrl;

	if (p == -1)
		return (-1);

	if(chan == NULL)
		return(p);

	ctrl = &isdn_ctrl[chan->ctrl];
	if (chan->plci == p)
	{
		if (ISBUSY(ctrl->appl))
		{
			isdn_disconn_ind(ctrl->appl);
			isdn_appl[ctrl->appl].ctrl = -1;
			isdn_appl[ctrl->appl].state = 0;
		}
		ctrl->appl = -1;
		ctrl->o_len = -1;
		chan->plci = -1;
		chan->ncci = -1;
		chan->state = DISCON;
		chan->i_len = 0;
		chan->more = 0;
	}
	return (p);
}

static int
sel_b2_prot_req(int unit, int c, int pl, dlpd_t * dlpd)
{
	return(en_q(unit, (c ? 0x40 : 0)| BD_SEL_PROT_REQ, 2, pl, sizeof(dlpd_t), (u_char *) dlpd));
}

static int
sel_b3_prot_req(int unit, int mb, u_short pl, ncpd_t * ncpd)
{
	return(en_q(unit, mb | BD_SEL_PROT_REQ, 3, pl, sizeof(ncpd_t), (u_char *) ncpd));
}

static int
discon_req(int w, int unit , int pl, int rea, int err)
{
	if((pl == 0) || (pl == -1))
		return(0);
	return(en_q(unit, DD_DISC_REQ,0, pl, 1, (u_char *) &rea));
}

static int
con_b3_resp(int unit, int mb, u_short ncci, u_short pl, u_char reject)
{
	u_char          buf[32];
	int             l = 4;

	bzero(buf, 32);
	*(u_short *) buf = ncci;
	buf[2] = reject;
	buf[3] = 0; /* ncpi ??? */
	l += 15;
	return(en_q(unit, mb | BD_CONN_B3_RSP,0, pl, l, buf));
}

static int
nnic_connect(int cn, int ap, int b_channel, int inf_mask, int out_serv
	    ,int out_serv_add, int src_subadr, unsigned ad_len
	    ,char *dest_addr, int spv)
{
	char            buf[128];

	if (ad_len > 118)
		return (-1);

	buf[0] = spv ? 0x53 : 0;
	buf[1] = b_channel;
	if (spv)
		inf_mask |= 0x40000000;
	*(u_long *) & buf[2] = inf_mask;
	buf[6] = out_serv;
	buf[7] = out_serv_add;
	buf[8] = src_subadr;
	buf[9] = ad_len;
	bcopy(dest_addr, &buf[10], ad_len);
	return (en_q(isdn_ctrl[cn].unit, DD_CONN_REQ, 0, MK_APPL(ap), ad_len + 10, buf));
}

static int
nnic_listen(int cn, int ap, int inf_mask, int subadr_mask, int si_mask, int spv)
{
	u_short         sbuf[4];

	if (spv)
		inf_mask |= 0x40000000;
	*(u_long *) sbuf = inf_mask;
	sbuf[2] = subadr_mask;
	sbuf[3] = si_mask;
	return (en_q(isdn_ctrl[cn].unit, DD_LISTEN_REQ, 0, MK_APPL(ap), 8, (u_char *) sbuf));
}

static int
nnic_disconnect(int cn, int rea)
{
	isdn_ctrl_t    *ctrl = &isdn_ctrl[cn];
	chan_t         *chan = &nnic_sc[ctrl->unit].sc_chan[C_CHAN(cn)];
	int             p, err;
	u_char		buf[16];

	if(chan->ncci != -1)
	{
		bzero(buf,16);
		*(u_short *) buf = chan->ncci;
		err= en_q(ctrl->unit, (C_CHAN(cn)?0x40:0)|BD_DISC_B3_REQ, 0
				, chan->plci, 3+sizeof(ncpi_t), buf);
		if((err==0) && (ctrl->o_len == 0))
			ctrl->o_len= -1;
		return(err);
	}
	p = chan->plci;
	if ((p == 0) || (p == -1))
		return (ENODEV);

	err= en_q(ctrl->unit, DD_DISC_REQ, 0, p, 1, (u_char *) &rea);
	if((err==0) && (ctrl->o_len == 0))
		ctrl->o_len= -1;
	return(err);
}

static int
nnic_accept(int cn, int an, int rea)
{
	isdn_ctrl_t    *ctrl = &isdn_ctrl[cn];
	struct nnic_softc *sc = &nnic_sc[ctrl->unit];
	chan_t         *chan = &sc->sc_chan[C_CHAN(cn)];
	isdn_appl_t    *appl  = &isdn_appl[an];

	if(ISFREE(ctrl->appl))
		return(ENODEV);

	if (rea)
	{
		ctrl->appl= -1;
		return(discon_req(1, ctrl->unit, chan->plci, rea, 0));
	}
	ctrl->appl= an;
	ctrl->lastact = time.tv_sec;
	appl->ctrl= cn;
	appl->state= 4;

	return(sel_b2_prot_req(ctrl->unit, C_CHAN(cn), chan->plci, &appl->dlpd));
}

static	int
nnicopen(dev_t dev, int flags, int fmt, struct proc *p)
{
	struct nnic_softc *sc;
	u_char          unit;
	int             x;
	unsigned	error;
	u_char	b= 0xff;

	unit = minor(dev);
	/* minor number out of limits ? */
	if (unit >= NNNIC)
		return (ENXIO);
	sc = &nnic_sc[unit];

	x= splhigh();
	/* Card busy ? */
/*
	if (sc->sc_flags & 7)
	{
		splx(x);
		return (EBUSY);
	}
*/
	sc->sc_flags |= OPEN;

	splx(x);
	return (0);
}

static	int
nnicclose(dev_t dev, int flags, int fmt, struct proc *p)
{
	struct nnic_softc *sc = &nnic_sc[minor(dev)];

	sc->sc_flags &= ~7;
	return (0);
}

static	int
nnicioctl(dev_t dev, int cmd, caddr_t data, int flags, struct proc *pr)
{
	int             error;
	int             i, x;
	struct nnic_softc *sc = &nnic_sc[minor(dev)];
	dpr_type       *dpr = sc->sc_dpr;
	mbx_type       *mbx= &dpr->dn_mbx;
	u_char	*p= (u_char *)dpr;
	struct head    *head = (struct head *) data;

	error = 0;
	switch (cmd)
	{
	case NICCY_DEBUG:
		data[0]= 0x39;
		bcopy(p, &data[1], 2044);
		break;
	case NICCY_LOAD:
		switch(head->status)
		{
		case 0: /* Loading initial boot code */
			x = splhigh();
			nnic_reset(sc,1);
			if(error = copyin(head->data+0x16,p, head->d_len-0x16))
			{
				splx(x);
				return (error);
			}
			nnic_reset(sc,0);
			i= hz;
			while (p[7] && i--)
			{
				error = tsleep((caddr_t) sc, PZERO | PCATCH, "l9_0", 1);
				if (error != EWOULDBLOCK)
				{
					splx(x);
					return (error);
				}
			}
			nnic_reset(sc,1);
			splx(x);
			if(p[7]) return(ENODEV);
			return(0);

		case 1: /* Loading boot code */
			x = splhigh();

			if(error = copyin(head->data+0x64,p, head->d_len-0x64))
			{
				splx(x);
				return (error);
			}
			nnic_reset(sc,0);
			i= 5*hz;
			while ((dpr->mainloop_cnt != 0x1147) && i--)
			{
				error = tsleep((caddr_t) sc, PZERO | PCATCH, "l9_1", 1);
				if (error != EWOULDBLOCK)
				{
					splx(x);
					return (error);
				}
			}

			if(dpr->mainloop_cnt != 0x1147)
			{
				splx(x);
				return(ENODEV);
			}

			i= 1*hz;
			while ((dpr->up_mbx.type != 1) && i--)
			{
				error = tsleep((caddr_t) sc, PZERO | PCATCH, "l9_2", 1);
				if (error != EWOULDBLOCK)
				{
					splx(x);
					return (error);
				}
			}
			if(dpr->up_mbx.type != 1)
			{
				splx(x);
				return(ENODEV);
			}
			bzero(&mbx->type, 16);
			dpr->up_mbx.msg_flag= 0;
			mbx->type= 0x21;
			mbx->msg_flag= 1;

			i= 1*hz;
			while (mbx->msg_flag && i--)
			{
				error = tsleep((caddr_t) sc, PZERO | PCATCH, "l9_3", 1);
				if (error != EWOULDBLOCK)
				{
					splx(x);
					return (error);
				}
			}
			if(mbx->msg_flag)
			{
				splx(x);
				return(ENODEV);
			}

			head->status= 0;
			splx(x);
			return(0);

		default:
			x = splhigh();
			while (mbx->msg_flag)
			{
				error = tsleep((caddr_t) sc, PZERO | PCATCH, "l9_1h", 1);
				if (error != EWOULDBLOCK)
				{
					splx(x);
					return (error);
				}
			}

			bzero(&mbx->type, 16);
			mbx->type = MD_DNL_MOD_REQ;
			mbx->subtype = head->typ;
			sc->sc_type = head->typ;
			mbx->data_len = 12;
			bcopy(head->nam, mbx->data, 8);
			*(u_long *) (mbx->data + 8) = head->len;

			mbx->msg_flag = 1;
			make_intr(sc);
			i= 1*hz;
			while ((dpr->up_mbx.msg_flag == 0) && i--)
			{
				error = tsleep((caddr_t) sc, PZERO | PCATCH, "l9_2", 1);
				if (error != EWOULDBLOCK)
				{
					splx(x);
					return (error);
				}
			}

			if((dpr->up_mbx.type != MU_DNL_MOD_CNF) || dpr->up_mbx.data[0])
			{
				dpr->up_mbx.msg_flag= 0;
				make_intr(sc);
				splx(x);
				return(ENODEV);
			}
			dpr->up_mbx.msg_flag= 0;
			make_intr(sc);
			{
				int len, l, off;
				len= head->d_len;
				off= 0;
				l= 0x64;

				while(len > 0)
				{
					while (mbx->msg_flag)
					{
						error = tsleep((caddr_t) sc, PZERO | PCATCH, "l9_4load", 1);
						if (error != EWOULDBLOCK)
						{
							splx(x);
							return (error);
						}
					}
					mbx->type = MD_DNL_MOD_DATA;
					len-= l;
					mbx->more_data = len > 0;
					mbx->data_len = l;
					if(error= copyin(head->data+off, mbx->data, l))
					{
						splx(x);
						return (error);
					}
					off+= l;
					l= min(len,512);
					mbx->msg_flag = 1;
					make_intr(sc);
				}
			}

			i= 3*hz;
			while ((dpr->up_mbx.msg_flag == 0) && i--)
			{
				error = tsleep((caddr_t) sc, PZERO | PCATCH, "l9_2", 1);
				if (error != EWOULDBLOCK)
				{
					splx(x);
					return (error);
				}
			}
			if(dpr->up_mbx.type == 0)
			{
				dpr->up_mbx.msg_flag= 0;
				make_intr(sc);
				i= 3*hz;
				while ((dpr->up_mbx.msg_flag == 0) && i--)
				{
					error = tsleep((caddr_t) sc, PZERO | PCATCH, "l9_3", 1);
					if (error != EWOULDBLOCK)
					{
						splx(x);
						return (error);
					}
				}
			}

			if(dpr->up_mbx.type != MU_DNL_MOD_IND)
			{
				dpr->up_mbx.msg_flag= 0;
				make_intr(sc);
				splx(x);
				return(ENODEV);
			}
			head->status = dpr->up_mbx.data[0];
			mbx->data[0] = dpr->up_mbx.data[1];
			mbx->type= 0x20|MU_DNL_MOD_IND;
			mbx->subtype = head->typ;
			mbx->data_len = 1;
			dpr->card_number = sc->sc_unit;
			dpr->up_mbx.msg_flag= 0;
			mbx->msg_flag= 1;
			make_intr(sc);
			splx(x);
			return (0);
		}
		splx(x);
		return(0);
	case NICCY_SET_CLOCK:
		x = splhigh();
		dpr->int_flg_pc = 0xff;
		dpr->card_number = sc->sc_unit;
		if (mbx->msg_flag)
		{
			splx(x);
			return (EBUSY);
		}
		bzero(&mbx->type, 16);
		mbx->type = MD_SET_CLOCK_REQ;
		mbx->data_len = 14;
		bcopy(data, mbx->data, 14);

		mbx->msg_flag = 1;
		make_intr(sc);
		splx(x);
		return (0);
	case NICCY_SPY:
		x = splhigh();
		if (mbx->msg_flag)
		{
			splx(x);
			return (EBUSY);
		}
		bzero(&mbx->type, 16);
		mbx->type = MD_MANUFACT_REQ;
		mbx->subtype = 18;
		mbx->data_len = 1;
		mbx->plci = MK_APPL(ispy_applnr);
/* There are ilegal states. So I use them to toggle */
		if((data[0] == 0) && (old_spy == 0)) data[0]= 255;
		else if(data[0] && old_spy ) data[0]= 0;
		old_spy= mbx->data[0]= data[0];

		mbx->msg_flag = 1;
		make_intr(sc);
		splx(x);
		return (0);

	case NICCY_RESET:
		x = splhigh();
		nnic_reset(sc,1);
		bzero((u_char*)dpr,DPR_LEN);
		sc->sc_flags= LOAD_ENTITY;
		splx(x);
		return (0);

	default:
		error = ENODEV;
	}
	return (error);
}

static void
dn_intr(unsigned unit, struct nnic_softc * sc)
{
	dpr_type       *dpr = sc->sc_dpr;
	mbx_type *mbx= &dpr->dn_mbx;
	chan_t         *chan;
	isdn_ctrl_t    *ctrl;
	int c,l, len;

	c= bsintr-1;
	chan = &sc->sc_chan[c];
	ctrl = &isdn_ctrl[chan->ctrl];

	if ((chan->state == ACTIVE) && (chan->more))
	{
		len= chan->more;

		bzero(&mbx->type, 20);
		mbx->type = BD_DATA_B3_REQ;
		if (c)
			mbx->type |= 0x40;
		*(u_short *) mbx->data = chan->ncci;
		mbx->data[4] = chan->msg_nr;
		l = min(DATAFIELD_LEN-5, len);
		mbx->data_len = l+5;
		bcopy(chan->more_b, &mbx->data[5], l);

		if (l < len)
		{
			chan->more = len - l;
			chan->more_b += l;
			mbx->more_data = 1;
		} else
		{
			chan->more = 0;
			ctrl->o_len = -1;
			bsintr= 0;
			++chan->msg_nr;
		}

		mbx->msg_flag = 1;
		make_intr(sc);
		ctrl->lastact = time.tv_sec;
		return;
	}
	bsintr= 0;
}

static void
up_intr(unsigned unit, struct nnic_softc * sc)
{
	dpr_type       *dpr = sc->sc_dpr;
	mbx_type *msg= &dpr->up_mbx;
	chan_t         *chan;
	u_short        n, mb, c, pl, err = 0;
	isdn_ctrl_t    *ctrl;
	isdn_appl_t    *appl;
	int error= 0;

	chan= NULL;
	ctrl= NULL;
	appl= NULL;
	mb= 0;
	pl = msg->plci;

	if(pl && (msg->type >= 0x40) && (msg->type < 0xfd) && (msg->type != 0x47))
	{
		if ((c = CHAN(pl)) < 2)
		{
			chan = &sc->sc_chan[c];
			ctrl = &isdn_ctrl[chan->ctrl];
		} else
		{
			c = 0xffff;
			chan= NULL;
			ctrl= NULL;
		}

		if(ctrl && (ctrl->appl & 0xC0) == 0)
			appl= &isdn_appl[ctrl->appl];
		else if( APPL(pl) < 0x30)
			appl = &isdn_appl[APPL(pl)];
		else if( APPL(pl) < 0x40)
			appl= NULL;
		else goto fin;

		if(msg->type >= 0x80)
		{
			mb= msg->type & 0x40;
			msg->type &= 0xbf;
		}
	}

	switch (msg->type)
	{
	case 0x01:		/* INIT IND */
		if(dpr->dn_mbx.msg_flag) return;
		error= en_q(unit,msg->type|0x20,0,0,0,NULL);
		break;
	case 0x04:		/* DNL MOD  */
		sc->sc_stat = msg->data[0];
		if (sc->sc_flags )
			sc->sc_flags = OPEN;
		break;
	case 0x06:		/* DNL MOD IND */
		if(dpr->dn_mbx.msg_flag) return;
		sc->sc_stat = msg->data[0];
		if (sc->sc_flags)
			sc->sc_flags = OPEN;
		if(sc->sc_stat)
			break;
		error= en_q(unit,msg->type|0x20,sc->sc_type,0,1, &msg->data[1]);
		break;
	case 0x0e:		/* SET CLOCK CONF */
		dpr->card_number = unit;
/*
		dpr->api_active   = 1;
		dpr->watchdog_cnt = 0xFF;
		dpr->api_area[0] = 0;
		dpr->api_area[2] = 0;
*/
		dpr->int_flg_pc   = 0xFF;
		break;
	case 0x15:		/* POLL IND */
		if(dpr->dn_mbx.msg_flag) return;
		dpr->api_active   = 1;
		dpr->watchdog_cnt = 0xFF;
		dpr->int_flg_pc   = 0xFF;
		error= en_q(unit,msg->type|0x20,0,0,0,NULL);
		break;
	case 0x16:		/* STATE IND */
		if(dpr->dn_mbx.msg_flag) return;
		if(sc->sc_flags & LOAD_ENTITY)
		{
			if(sc->sc_flags & 7)
				sc->sc_flags = OPEN;
			else sc->sc_flags= 0;
		}
		error= en_q(unit,msg->type|0x20,0,0,0,NULL);
		break;
	case 0x17:		/* STATE RESP */
		break;
	case 0x1e:              /* MANUFACT CONF */
		if(msg->subtype == 18)
			break;
		badstate(msg,1,0,dpr);
		break;
	case 0x1f:              /* MANUFACT IND */
		if(msg->subtype == 19)
		{
			if(dpr->dn_mbx.msg_flag) return;
			isdn_input(ispy_applnr, msg->data_len, msg->data,0);
			error= en_q(unit,msg->type|0x20,msg->subtype,0,0,NULL);
			break;
		}
		badstate(msg,2,0,dpr);
		break;
	case 0x40:		/* CONNECT CONF */
		err = *(u_short *) msg->data;
		if (err || (appl == NULL) || (chan == NULL) || (ctrl == NULL))
		{
			if(chan) reset_plci(3, chan, pl);
			if(appl) appl->state= 0;
			break;
		}
		if (ISBUSY(ctrl->appl))
		{
			if(dpr->dn_mbx.msg_flag) return;
			error= discon_req(2, unit, pl, 0, 0);
			break;
		}
		chan->plci = pl;
		chan->msg_nr = 0;
		chan->ncci = -1;
		ctrl->lastact = time.tv_sec;
		ctrl->appl = APPL(pl);
		appl->ctrl = chan->ctrl;
		ctrl->islisten= 0;
		chan->state = DIAL;
		appl->state= 3;
		break;

	case 0x41:		/* CONNECT IND */
		if (ISBUSY(ctrl->appl))
		{
			if(dpr->dn_mbx.msg_flag) return;
			error= discon_req(3, unit, pl, 0, 0);
			break;
		}
		chan->plci = pl;
		chan->msg_nr = 0;
		chan->ncci = -1;
		ctrl->lastact = time.tv_sec;
		ctrl->appl = 0x7f;
		ctrl->islisten= 1;
		chan->state = CALLED;
		msg->data[msg->data[3] + 4] = 0;
		isdn_accept_con_ind(APPL(pl), chan->ctrl, msg->data[0], msg->data[1]
		       ,msg->data[2], msg->data[3], (char *) &msg->data[4]);
		break;

	case 0x42:		/* CONNECT ACTIVE IND */
		if(dpr->dn_mbx.msg_flag) return;
		error= con_act_resp(unit, pl);
		if (IS_LISTEN(pl))
		{
			isdn_conn_ind(ctrl->appl,chan->ctrl,0);
			break;
		}
		isdn_conn_ind(APPL(pl),chan->ctrl,1);
		chan->state = CONNECT;
		ctrl->appl = APPL(pl);
		appl->ctrl = chan->ctrl;
		break;

	case 0x43:		/* DISCONNECT CONF */
		reset_plci(4, chan, pl);
		break;

	case 0x44:		/* DISCONNECT IND */
		if(dpr->dn_mbx.msg_flag) return;
		error= discon_resp(unit, reset_plci(5, chan, pl));
		break;

	case 0x47:		/* LISTEN CONF */
		isdn_state = *(u_short *) msg->data;
		break;

	case 0x4a:		/* INFO IND */
		if(dpr->dn_mbx.msg_flag) return;
		isdn_info(APPL(pl),*(u_short *)msg->data, msg->data[2], msg->data+3);
		error= inf_resp(unit, pl);
		break;
	case 0x80:	/* SELECT PROT CONF */
		if(dpr->dn_mbx.msg_flag) return;
		err = *(u_short *) msg->data;
		if (err)
		{
			error= discon_req(4, unit, pl, 0, err);
			break;
		}

		switch (msg->subtype)
		{
		case 2:/* SELECT B2 PROTOCOL */
			if(ISFREE(ctrl->appl))
				break;
			error= sel_b3_prot_req(unit, mb, pl, &isdn_appl[ctrl->appl].ncpd);
			break;

		case 3:/* SELECT B3 PROTOCOL */
			if (IS_DIAL(pl))
				error= con_b3_req(unit, mb, pl);
			else
				error= listen_b3_req(unit, mb, pl);
			break;
		}
		break;

	case 0x81:	/* LISTEN B3 CONF */
		if(dpr->dn_mbx.msg_flag) return;
		err = *(u_short *) msg->data;
		if (err)
		{
			error= discon_req(5, unit, pl, 0, err);
			break;
		}
		error= con_resp(unit, pl, 0);
		break;

	case 0x82:	/* CONNECT B3 CONF */
		err = *(u_short *) (msg->data + 2);
		n = *(u_short *) msg->data;

		if (err)
		{
			if(dpr->dn_mbx.msg_flag) return;
			error= discon_req(6, unit, pl, 0, err);
			break;
		}
		if(ISFREE(ctrl->appl))
			break;
		chan->ncci = n;
		chan->state = CONNECT;
		break;

	case 0x83:	/* CONNECT B3 IND */
		if(ISFREE(ctrl->appl))
			break;
		if(dpr->dn_mbx.msg_flag) return;
		n = *(u_short *) msg->data;
		chan->ncci = n;
		chan->state = CONNECT;
		error= con_b3_resp(unit, mb, n, pl, 0);
		break;

	case 0x84:	/* CONNECT B3 ACTIVE IND */
		if(ISFREE(ctrl->appl))
			break;
		if (chan->state < IDLE)
		{
			chan->state = IDLE;
			ctrl->o_len = 0;
			/*
			 * XXX the chan->ctrl arg is very bogus.
			 * Don't just use a cast to "fix" it.
			 */
			timeout(isdn_start_out, chan->ctrl, hz / 5);
		}
		break;

	case 0x85:	/* DISCONNECT B3 CONF */
		if(ISBUSY(ctrl->appl))
			chan->state = ISDISCON;
		err = *(u_short *) (msg->data + 2);
		if (err)
		{
			if(dpr->dn_mbx.msg_flag) return;
			error= discon_req(7, unit, pl, 0, err);
			break;
		}
		break;
	case 0x86:	/* DISCONNECT B3 IND */
		if(dpr->dn_mbx.msg_flag) return;
		if(ISBUSY(ctrl->appl))
			chan->state = ISDISCON;
		err = *(u_short *) (msg->data + 2);
		error= discon_req(8, unit, pl, 0, err);
		break;

	case 0x88:	/* DATA B3 CONF */
		if(ISFREE(ctrl->appl))
			break;
		err = *(u_short *) (msg->data + 2);
		if (err)
		{
			ctrl->send_err++;
			isdn_appl[ctrl->appl].send_err++;
		}
		chan->state = IDLE;
		ctrl->o_len = 0;
		isdn_start_out(chan->ctrl);
		break;

	case 0x89:	/* DATA B3 IND */
		if(ISFREE(ctrl->appl))
			break;
		if (msg->more_data)
		{
			if(chan->i_len)
			{
				if((chan->morenr != msg->data[4]) || ((chan->i_len + msg->data_len - 5) > 2048))
					break;
			}
			else
				chan->morenr= msg->data[4];
			bcopy(msg->data+5, &chan->i_buf[chan->i_len], msg->data_len-5);
			chan->i_len += msg->data_len -5;
			break;
		} /* msg->more_data == 0 */
		if (chan->i_len)
		{
			int l;

			if(chan->morenr != msg->data[4])
				break;

			if ((l = chan->i_len + msg->data_len - 5) <= 2048)
			{
				bcopy(msg->data+5, &chan->i_buf[chan->i_len], msg->data_len);
				if(isdn_input(ctrl->appl, l, chan->i_buf, ctrl->islisten))
					ctrl->lastact = time.tv_sec;
			}
			chan->i_len = 0;
			break;
		} /* chan->i_len == 0 && msg->more_data == 0 */
		if(isdn_input(ctrl->appl, msg->data_len-5, msg->data+5,ctrl->islisten))
			ctrl->lastact = time.tv_sec;
		break;

	default:
		badstate(msg,3,mb,dpr);
		break;
	}

fin:
	if(error)
	{
printf("E?%x",error);
		return;
	}
	msg->msg_flag= 0;
	timeout(make_intr, (void *)sc,1);
}

static void
nnnicintr(void *gen)
{
	unsigned int unit = (int)gen;
	register struct nnic_softc *sc = &nnic_sc[unit];
	dpr_type       *dpr = sc->sc_dpr;

	if(dpr->up_mbx.msg_flag)
		up_intr(unit,sc);
	if (bsintr && (dpr->dn_mbx.msg_flag == 0))
		dn_intr(unit,sc);
}
void
nnicintr(int unit)
{
        timeout(nnnicintr, (void *)unit,1);
}


static nnic_devsw_installed = 0;

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

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

SYSINIT(nnicdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,nnic_drvinit,NULL)

#endif				/* NNNIC > 0 */
