4.4BSD/usr/src/sys/vax/if/ACC/driver/if_pi.c

Compare this file to the similar file:
Show the results in this format:

/*************************************************************************/
/*                                                                       */
/*                                                                       */
/*       ________________________________________________________        */
/*      /                                                        \       */
/*     |          AAA          CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*     |         AAAAA        CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     |        AAAAAAA       CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |       AAAA AAAA      CCCC              CCCC              |      */
/*     |      AAAA   AAAA     CCCC              CCCC              |      */
/*     |     AAAA     AAAA    CCCC              CCCC              |      */
/*     |    AAAA       AAAA   CCCC              CCCC              |      */
/*     |   AAAA  AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |  AAAA    AAAAAAAAAAA CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     | AAAA      AAAAAAAAA   CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*      \________________________________________________________/       */
/*                                                                       */
/*  	Copyright (c) 1989 by Advanced Computer Communications           */
/*  	720 Santa Barbara Street, Santa Barbara, California  93101       */
/*  	(805) 963-9431                                                   */
/*                                                                       */
/*                                                                       */
/*  Files:		if_pi.c, if_pivar.c                              */
/*			ACP_PI (Programmer Interface) for 4.3bsd and	 */
/*			Ultrix 2.0 (and newer)				 */
/*                                                                       */
/*  Author:		??? (Steve or Charles)				 */
/*                                                                       */
/*  Project:		Programmers Interface for 6250 software		 */
/*                                                                       */
/*  Function:		To enable network connections on ACP_PI to	 */
/*			communicate with UNIX.				 */
/*									 */
/* Configuration Entry:							 */
/*									 */
/*      device dda0 at uba? csr 0166740 vector ddainta ddaintb		 */
/*									 */
/* 									 */
/*  Revision History at end of file					 */
/*************************************************************************/

/*************************************************************************/
/*									 */
/* Usage Notes:								 */
/*									 */
/*      - make devices in /dev for those pi				 */
/*        devices which you want in your configuration			 */
/* 									 */
/* System Notes:							 */
/* 									 */
/*       Refer to the installation instructions, readme.txt, which	 */
/*       are included on the pi driver distribution medium.		 */
/*									 */
/* Design Overview:							 */
/*									 */
/* Data flows in two directions through the PI interface.  This section	 */
/* covers how data gets in and out of the driver.			 */
/*									 */
/* OUTBOUND DATA (user process -> FE):					 */
/*									 */
/* 1) The user process issues an ioctl(2) call with pi_dblock  structure */
/*    which gives the address and length of the user data as well as	 */
/*    the lcn that the data is to go out on.				 */
/* 2) The piioctl routine in the case XIOWRITE checks the validity of    */
/*    the specified lcn the calls pi_write.				 */
/* 3) The pi_write routine allocates a small mbuf.  If the user data	 */
/*    is longer than MLEN (112) bytes then a page cluster is allocated	 */
/*    to convert the small mbuf into a large mbuf.  The user data is	 */
/*    then copied to the mbuf.  The mbuf is put on the output queue for  */
/*    the lcn (dc->dc_oq) using the IF_ENQUEUE macros.  If the queue is  */
/*    full then the process will sleep on dc->dc_oq.  The wakeup will    */
/*    come from the pi_data routine when it sees a write completion.     */
/*    After the data is queue the dda_start is called to try to send the */
/*    data.								 */
/* 4) The dda_start routine dequeues the data from the per lcn ouput     */
/*    data queue and hooks the mbuf onto the write side of the hdx_chan  */
/*    structure for the lcn. The routine dda_wrq is called passing this  */
/*    structure.							 */
/* 5) The routine dda_wrq takes the hc structure and puts it on the      */
/*     global output queue ds->dda_sioq. It then calls start_chn().	 */
/* 6) start_chn() set up the comregs and request an A interrupt.  The    */
/*    A interrupt routine will handle the transfer grant and write 	 */
/*    completion.							 */
/*									 */
/* INBOUND DATA (FE -> user process):					 */
/*									 */
/* A) interrupt side----						 */
/*    1) Initially a read is posted for every logical circuit.  When     */
/*	 data comes in the interrupt A routine will handle doing the	 */
/*	 transfer grant. When the read completes then the type of	 */
/*	 channel is determined (IP,X.29 or PI).  For PI data the routine */
/*	 pi_data() is called with the mbuf and read completion status.   */
/*    2) In the routine pi_data() if the read completion status is       */
/*	 DDAIOCOK then the the packet is complete so the routine	 */
/*	 pi_queue_data is called to queue the data for the user process  */
/*	 to read.  If the status is DDAIOCOKP then the packet has not	 */
/*	 been completely read.  In this case the routine dda_rrq is	 */
/*	 called to allocate a new mbuf and chain it to the end of the	 */
/*	 mbufs that have already been filled from this packet.  When the */
/*	 packet is exhausted then the whole chain of mbufs will be 	 */
/*	 passed to pi_queue_data.					 */
/*    3) The routine pi_queue_data uses a small array of pi_qmem	 */
/*	 structures to form a queue of input data.  Each of these	 */
/*	 structures has a pointer to an mbuf.  This may point to a	 */
/*	 single mbuf or a chain of mbufs depending on how many mbufs it	 */
/*	 took to read the packet.  Pi_queue_data will copy the important */
/*	 information to the pi_qmem structure and then wakeup any	 */
/*	 processes that have slept waiting for data to come in.		 */
/*									 */
/* B) system call side----						 */
/*    1) The user process issues the XIOREAD ioctl with a pi_dblock	 */
/*	structure describing the target buffer.				 */
/*    2) the piioctl routine using the XIOREAD case calls the routine	 */
/*       pi_rem_qdata to get the next data item.  If there are no data	 */
/*       items ready then pi_rem_qdata will return -1  and the process	 */
/*       will sleep waiting for data to come in.  The wakeup will be done*/
/*       in pi_queue_data.  When a data item is dequeued from a channel	 */
/*       that is not the supervisor channel then a new read is issued on */
/*       that logical circuit.  This means that at most 1 data item will */
/*       be present in the input queue for a particular data circuit.    */
/*	 This excludes the supervisory circuit 0.			 */
/*    3) the pi_rem_qdata routine will dequeue  an mbuf and copy the	 */
/*	 data to the user buffer along with other information such as	 */
/*	 the lcn and read completions statuses.  If several mbufs were	 */
/*	 used to hold a single packet then the entire chain will be	 */
/*	 scanned and all the data placed in the user buffer.		 */
/*************************************************************************/

#ifdef SIMULATION
#include "if_pivar.h"
#else
#include "../vaxif/if_pivar.h"
#endif

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   LOCAL  FUNCTIONS                          %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void    pi_init ();
PRIVATE void    pi_init_single ();
PRIVATE void    pi_clear_piq ();
PRIVATE void    pi_clear_single ();
PRIVATE void    pi_free_all_lcns ();
PRIVATE void    pi_queue_data ();
PRIVATE int     pi_rem_qdata ();
PRIVATE int     pi_find_chan_for_ring ();
PRIVATE int     pi_write ();

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   LOCAL  VARIABLES                          %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/* PILINES: number of channels (minor device numbers) allocated per board */
/* PIQLEN: max number of messages that can be queued for a channel */
#define PILINES		32
#define PIQLEN		8

#define NPILINES        (NDDA * PILINES)

#ifdef DDADEBUG
int pi_placemark = 0;
#define PI_PLACEMARK(n)	(pi_placemark = (n))
#else
#define PI_PLACEMARK(n)
#endif

/* the pi_qmem structure is used to hold essential information about
 * incomming data until the user process has a chance to issue a read.
 * These structures form a queue of input data. All fields should be 0
 * when the structure is not actively part of the queue.
 */
struct pi_qmem
{
    struct mbuf    *mb;
    u_char          stat;
    u_char          substat;
    u_char          lcn;
};


/* the pi_info structure describes a data channel which corresponds to a
 * minor device.  The values in the protocols fields are only valid when
 * the PI_ACCEPT_RING bit is set in flags.  The 0 element is the lowest
 * protocol, The 1 element is the highest.
 */

struct pi_info
{
    u_short         flags;
    u_int           pgrp;	/* process group */
    u_int           signal;	/* signal to use upon data ready */
    struct mbuf    *msav;	/* place to hang mbuf waiting to go out */
    struct pi_qmem  pi_q[PIQLEN];	/* queue of pending input data */
    u_char          firstq, lastq;	/* index of first and last queue
					 * members */
    u_char          pi_qlen;	/* length of the input queue */
    u_char          protocols[2];	/* low and high protocol range */
};

/* bits for the flags field */
#define PI_ACCEPT_RING	0x1
#define PI_ACCEPT_ANY   0x2

struct pi_info  pi_info[NPILINES];	/* tty structures */

/* this macro gives the channel (minor device number) associated with
 * an active lcn.
 */
#define CHANNEL(lcn,ds)		((ds)->dda_cb[(lcn)].dc_line)

/*
 * macro to translate a device number to the unit (i.e. ACP_PI)
 * with which it is associated,  M001 use V7 major and minor macros
 */

#define PI_UNIT(x) (minor(x) / PILINES)	/* ACP_PI controlling this line */
#define PI_LINE(x) (minor(x) % PILINES)	/* Line number */

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   GLOBAL ROUTINES                           %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       PIIOCTL()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Process ioctl request.                                         */
/*                                                                 */
/*  Call:           piioctl(dev, cmd, data, flag)                  */
/*  Argument:       dev:   device                                  */
/*                  cmd:   ioctl command                           */
/*                  data:  pointer to data                         */
/*                  flag:  ignored                                 */
/*  Returns:        0 for sucess, else nonzero error code          */
/*  Called by:      kernel software software,  this routine is in  */
/*                  the cdevsw table                               */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

piioctl (dev, cmd, data, flag)
dev_t           dev;
caddr_t         data;
{
    struct dda_cb  *dc;
    struct dda_softc *ds;
    int             l, unit, maxlcn;

    l = PI_LINE (dev);
    unit = PI_UNIT (dev);
    maxlcn = nddach[unit];
    ds = &dda_softc[unit];
    switch (cmd)
    {
    case XIOWRITE:
	{
	    struct pi_dblock *pd;

	    pd = (struct pi_dblock *) data;
	    if (pd->lcn > maxlcn)
	    {
		DMESG(unit, 66,
		    (DDALOG(LOG_ERR)
			 "XIOWRITE: invalid lcn %d\n", pd->lcn
		     DDAELOG));
		return (EINVAL);
	    }
	    if (pd->lcn && (CHANNEL (pd->lcn, ds) != l))
	    {
		DMESG (unit, 67, 
		    (DDALOG(LOG_ERR)
			"XIOWRITE: lcn %d not associated with channel %d.\n",
			 pd->lcn, l
		    DDAELOG));
		return (EINVAL);
	    }
	    return (pi_write (ds, l, pd));
	}
    case XIOREAD:
	{
	    int             ret;
	    struct pi_dblock *pd;

	    pd = (struct pi_dblock *) data;
	    while (ret = pi_rem_qdata (l, pd))
	    {
		if (ret > 0)
		    goto newread;
		else
		if (pd->flags & DB_NONBLOCK)
		    return (EWOULDBLOCK);	/* read already posted */
		else
		{
#ifdef DDA_MSGQ
		    dda_mqstr ("(r sl) ");
#endif
		    sleep (pi_info[l].pi_q, TTIPRI);
#ifdef DDA_MSGQ
		    dda_mqstr ("(r wo) ");
#endif
		}
	    }
    newread:
#ifdef DDA_MSGQ
	    dda_mqstr ("(r go) ");
#endif
	    if (pd->lcn)	/* issue new read for the lcn */
		dda_rrq (ds, &ds->dda_cb[pd->lcn].dc_rchan);
	    return (ret);
	}
    case XIORPEND:
	{
	    int             s;

	    s = splimp ();
	    *((int *) data) = pi_info[l].pi_qlen;
	    splx (s);
	    break;
	}
    case XIOACCRING:
	{
	    struct proto_range *pr;
	    int             s;

	    pr = (proto_range *) data;
	    s = splimp ();
	    pi_info[l].flags |= PI_ACCEPT_RING;
	    pi_info[l].protocols[0] = pr->lower;
	    pi_info[l].protocols[1] = pr->upper;
	    splx (s);
	    break;
	}
    case XIOANYPROTO:
	pi_info[l].flags |= PI_ACCEPT_ANY;
	break;
    case XIOFREELCN:
	{
	    u_char          lcn;

	    lcn = *((u_char *) data);
	    if (lcn > maxlcn)
		return EINVAL;
	    dc = &ds->dda_cb[lcn];
	    if (CHANNEL (lcn, ds) != l)
		return EINVAL;
	    else
	    {
		abort_io (unit, lcn);
		dc->dc_state = LC_IDLE;
		dc->dc_line = -1;
		dc->dc_inaddr.s_addr = 0;	/* forget address */
		dc->dc_key.key_addr.s_addr = 0;
		dc->dc_wsizein = dc->dc_wsizeout = 0;
		dc->dc_pktsizein = dc->dc_pktsizeout = 0;
		dc->dc_flags = 0;
	    }
	    break;
	}
    case XIOABORT:
	{
	    u_char          lcn;

	    lcn = *((u_char *) data);
	    if (lcn > maxlcn)
		return EINVAL;
	    dc = &ds->dda_cb[lcn];
	    if (CHANNEL (lcn, ds) != l)
		return EINVAL;
	    else
		abort_io (ds->dda_if.if_unit, lcn);
	    break;
	}
    case XIOGETLCN:

	/*
	 * find a free lcn.  Init the line field for this minor device. stuff
	 * lcn number in struct. init the circuit state. 
	 */
	if (dc = find_free_lcn (ds))
	{
	    dc->dc_flags = DC_RAW;
	    dc->dc_state = LC_DATA_IDLE;
	    dc->dc_line = l;
	    *data = dc->dc_lcn;
	}
	else
	    *data = 0;
	break;
    case XIOCLRCHAN:
	pi_clear_piq (ds, &pi_info[l]);
	break;
    case XIONORING:
	pi_info[l].flags &= ~(PI_ACCEPT_ANY | PI_ACCEPT_RING);
	break;
    case XIORSIG:
	pi_info[l].signal = *((int *) data);
	break;
	/* case XIOABORTIO: do an abort-io on the channel */
    default:
	return (ENOTTY);
    }
    return (0);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       PIOPEN()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Open a line.                                                   */
/*                                                                 */
/*  Call:           piopen(dev, flag)                              */
/*  Argument:       dev:   device                                  */
/*                  flag:  indicates type of open, "nonblocking"   */
/*                         "or block if in use"                    */
/*  Returns:        0 for success, else nonzero error code         */
/*  Called by:      kernel software software,  this routine is in  */
/*                  the cdevsw table                               */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

piopen (dev, flag)
dev_t           dev;
int             flag;
{
    int             unit, d;

    unit = PI_UNIT (dev);
    d = PI_LINE (dev);

    if (d >= NPILINES)
	return (ENXIO);

    /* wait for interface to come up */
    while (dda_softc[unit].dda_state != S_LINK_UP)
	sleep (&dda_softc[unit].dda_state, TTIPRI);

    if (pi_info[d].pgrp == 0)
	pi_info[d].pgrp = u.u_procp->p_pid;
    return (0);
}


/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       PICLOSE()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Close a line.                                                  */
/*                                                                 */
/*  Call:           piclose(dev, flag)                             */
/*  Argument:       dev:   device                                  */
/*                  flag:  unused                                  */
/*  Returns:        nothing                                        */
/*  Called by:      kernel software software,  this routine is in  */
/*                  the cdevsw table                               */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

piclose (dev, flag)
dev_t           dev;
int             flag;
{
    pi_clear_single (&dda_softc[PI_UNIT (dev)], &pi_info[PI_LINE (dev)], 0);
    pi_free_all_lcns (PI_UNIT (dev), PI_LINE (dev));
}


/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   LOCAL  FUNCTIONS                          %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      PI_SUPR()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*       This routine processes received supervisor messages.      */
/*       Depending on the message type, the appropriate action is  */
/*       taken.                                                    */
/*                                                                 */
/*  Call:              pi_supr(ds, mb)                             */
/*  Arguments:         ds:  pointer to dev control block struct    */
/*                     mb:  pointer to mbuf 			   */
/*                              containing the supervisor message  */
/*  Returns:           nothing                                     */
/*  Called by:         dda_supr()                                  */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_supr (ds, mb)
struct dda_softc *ds;
struct mbuf    *mb;

{
    u_char         *p;
    register int    lcn;
    register int    chan;
    int             unit;

    unit = ds->dda_if.if_unit;
#ifdef DDADEBUG
    if (DDADBCH (21, unit))
	DDALOG(LOG_ERR) "dda%d:(pi) pi_supr()\n", unit DDAELOG;
#endif DDADEBUG
    p = mtod (mb, u_char *);
    switch (p[0])
    {
    case LINE_STATUS:		/* link status msg */
    case RESTART:		/* restart received */
    case RSTRT_ACK:		/* restart ack */
    case STATRESP:		/* Statistics Response from FEP */
    case CLEARVC:		/* clear by VCN */
	DMESG (unit, 64,
	    (DDALOG(LOG_ERR)
		"dda%d:(pi) pi_supr: unexpected msg type 0x%x\n",
		unit, p[0]
	    DDAELOG));
	break;

    case RING:			/* incoming call */
	if ((chan = pi_find_chan_for_ring (p)) >= 0)
	    pi_queue_data (unit, chan, 0, 0, 0, mb);
	else			/* if no willing channel's */
	{
	    /* reject the call */
	    send_supr (ds, CLEARVC, p[2], 0);	/* clear call */
	    if (LOG_CALLS)
		DDALOG(LOG_ERR)
		    "dda%d:(pi) Call REJECTED VC 0x%x\n", unit, p[1]
		DDAELOG;
	}
	break;
    case ANSWER:		/* call answered */
    case CLEARLC:		/* clear by LCN */
    case RESET:		/* X25 reset */
    case INTERRUPT:		/* X25 interrupt */
    case INTR_ACK:
	lcn = p[1] / 2;		/* get LCN */
	pi_queue_data (unit, CHANNEL(lcn, ds), 0, 0, 0, mb);
	break;
    default:
	DMESG(unit, 65, 
	    (DDALOG(LOG_ERR)
		"dda%d:(pi) supervisor error (%x %x %x %x)\n",
		unit, p[0], p[1], p[2], p[3]
	    DDAELOG));
    }
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       PI_DATA()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine is called when a data channel I/O completes.    */
/*    If the completion was for a write, an attempt is made to     */
/*    start output on the next packet waiting for output on that   */
/*    LCN.  If the completion was for a read, the received packet  */
/*    is sent to the pi input queue (if no error).                 */
/*                                                                 */
/*  Call:              pi_data(ds, hc, cc, cnt, subcc)             */
/*  Argument:          ds:  device control block                   */
/*                     hc:  half duplex channel control block      */
/*                     cc:   Mailbox I/O completion status         */
/*                     cnt:  byte count                            */
/*		       subcc: Mailbox I/O completion substatus     */
/*  Returns:           nothing                                     */
/*  Called by:         ddainta()                                   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_data (ds, hc, cc, cnt, subcc)
register struct dda_softc *ds;
register struct hdx_chan *hc;
int             cc, cnt, subcc;
{
    register struct dda_cb *dc = &(ds->dda_cb[hc->hc_chan / 2]);
    int             unit;

    unit = ds->dda_if.if_unit;
#ifdef DDADEBUG
    if (DDADBCH (18, unit))
	DDALOG(LOG_ERR)
	    "dda%d:(pi) pi_data: chan=0x%x  cc=0x%x  cnt=0x%x subcc=0x%x\n",
	    unit, hc->hc_chan, cc, cnt, subcc
	DDAELOG;
#endif DDADEBUG

#ifdef DDA_MSGQ
	dda_mqstr("(dt");
	dda_mqnum(hc->hc_chan,MQHEX); dda_mqstr(" ");
	dda_mqnum(cc,MQHEX); dda_mqstr(" ");
	dda_mqnum(cnt,MQHEX); dda_mqstr(" ");
	dda_mqstr(")");
#endif
    if (hc->hc_chan & 0x01)	/* was it read or write? */
    {				/* write, fire up next output */
#ifdef DDADEBUG
	dc->dc_out_t = TMO_OFF;	/* turn off output completion timer */
#endif
	if ((hc->hc_func != DDAABT) && (hc->hc_curr = hc->hc_curr->m_next))
	    dda_wrq (ds, hc, 0);
	else
	{
	    m_freem (hc->hc_mbuf);
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	    if (hc->hc_func == DDAABT)
	    {
		hc->hc_func &= ~DDAABT;
		hc->hc_sbfc &= ~INVALID_MBUF;
	    }
	    else
		ds->dda_if.if_opackets++;
	    dc->dc_flags &= ~DC_OBUSY;
#ifdef DDA_MSGQ
	    dda_mqstr ("(w wa) ");
#endif
	    wakeup (&(dc->dc_oq));	/* wake up anyone sleeping on output */
	    dda_start (ds, dc);	/* and try to output */
	    dc->dc_timer = TMO_OFF;	/* reset timer */
	}
    }
    else			/* read, process rcvd packet */
    {
#ifdef DDADEBUG
#ifdef notdef
	if (DDADBCH (19, unit))
	{
	    u_char         *p;

	    DDALOG(LOG_ERR) "dda%d:(pi) ", unit DDAELOG;
	    p = mtod (hc->hc_curr, u_char *);
	    prt_bytes (p, (cnt < 56 ? cnt : 56));
	    DDALOG(LOG_ERR) "\n" DDAELOG;
	}
#endif
#endif DDADEBUG

	if (cc == DDAIOCOK)
	{			/* Queue good packet for input */
	    hc->hc_curr->m_len += cnt;	/* update byte count */

	    ds->dda_if.if_ipackets++;
	    pi_queue_data (unit, dc->dc_line, dc->dc_lcn, cc, subcc, hc->hc_mbuf);
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	}
	else
	if (cc == DDAIOCOKP)	/* more data pending in this packet */
	{
	    hc->hc_curr->m_len += cnt;	/* update byte count */
	    dda_rrq (ds, hc);
	}
	else
	{
	    m_freem (hc->hc_mbuf);
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	    dda_rrq (ds, hc);
	}
	/* don't hang new data read.  This is done in the user read ioctl */
    }
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       PI_INIT()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This function initializes the pi_info structure.  The      */
/*	active flag is non-zero if the interface is active and got */
/*	a reset.  In this case pi_clear_single is called which     */
/*	free any resources held and send a hangup to the           */
/*	controlling process.                                       */
/*                                                                 */
/*  Call:           pi_init(unit, active)                          */
/*  Argument:       unit: unit to be initialized.                  */
/*                  active: non-zero if the interface is active    */
/*                          when the reset is issued.              */
/*  Returns:        nothing.                                       */
/*  Called by:      ddareset(), ddainit(), bufreset().             */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_init (unit, active)
int             unit;
int             active;
{
    struct pi_info *pi, *end;
    struct dda_softc *ds;

    ds = &dda_softc[unit];
    end = pi_info + ((unit + 1) * PILINES);
    for (pi = end - PILINES; pi < end; pi++)
	if (active)
	    pi_clear_single (ds, pi, 1);
	else
	    pi_init_single (pi);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                    PI_INIT_SINGLE()                         %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	Initialize a pi_info structure.  For now all this means is */
/*	zeroing out everything.	This routine is here to make future*/
/*	enhancements easier.				           */
/*                                                                 */
/*  Call:           pi_init_single (pi)                            */
/*  Argument:       pi: pointer to pi_info structure               */
/*  Returns:        nothing.                                       */
/*  Called by:      pi_init(), pi_clear_single().                  */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_init_single (pi)
struct pi_info *pi;
{
    bzero ((char *) pi, sizeof (struct pi_info));
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                 PI_CLEAR_PIQ()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This routine will free up all the mbufs that are queued on */
/*	the pi_q.  Note that this routine assumes that any pi_q    */
/*	element is zeroed when not in use.  			   */
/*                                                                 */
/*  IMPORTANT: this routine reissues reads for all the lcn         */
/*	whose data is removed from the queue.  This parallels what */
/*	is done when the data is dequeue normally by XIOREAD       */
/*                                                                 */
/*  Call:           pi_clear_piq (ds, pi)                          */
/*  Argument:       ds: pointer to softc used to call dda_rrq.     */
/*                  pi: pointer to pi_info structure.              */
/*  Returns:        nothing                                        */
/*  Called by:      pi_clear_single(), piioctl() case XIOCLRCHAN   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_clear_piq (ds, pi)
struct dda_softc *ds;
struct pi_info *pi;
{
    register int    i;

    for (i = 0; i < PIQLEN; i++)
	if (pi->pi_q[i].mb)
	{
	    m_freem (pi->pi_q[i].mb);
	    if (pi->pi_q[i].lcn)/* issue new read for the lcn */
		dda_rrq (ds, &ds->dda_cb[pi->pi_q[i].lcn].dc_rchan);
	}
    bzero ((char *) pi->pi_q, sizeof (pi->pi_q));
    pi->pi_qlen = 0;
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                  PI_CLEAR_SINGLE()                          %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	this routine will re-initialize a single pi_info structure.*/
/*	If hangup is non-zero then a hangup signal will be sent to */
/*	the controlling process.                                   */
/*                                                                 */
/*  Call:           pi_clear_single (ds, pi, hangup)               */
/*  Argument:       ds: pointer to dda_softc structure             */
/*                  pi: pointer to pi_info structure               */
/*                  hangup: non-zero if hangup is to be sent.      */
/*  Returns:        nothing.                                       */
/*  Called by:      piclose(), pi_init().                          */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/* reset a pi_info structure that is active.  This includes freeing any
 * queue mbufs and sending a hangup to the controlling process
 */
PRIVATE void
pi_clear_single (ds, pi, hangup)
struct dda_softc *ds;
struct pi_info *pi;
int             hangup;		/* non-zero if a hangup is to be sent to
				 * controlling process */
{
    pi_clear_piq (ds, pi);
    if (hangup && pi->pgrp)
    {
	gsignal (pi->pgrp, SIGHUP);
	gsignal (pi->pgrp, SIGCONT);
    }
    if (pi->msav)
	m_freem (pi->msav);
    pi_init_single (pi);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                 PI_FREE_ALL_LCNS ()                         %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	this routine will free all logical circuits associated with*/
/*	a particular channel.  This should return all mbufs that   */
/*	Are used by the channel.                                   */
/*                                                                 */
/*  Call:           pi_free_all_lcns (unit,chan)                   */
/*  Argument:       chan: channel number.                          */
/*		    unit: unit number.                             */
/*  Returns:        nothing.                                       */
/*  Called by:      piclose()                                      */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_free_all_lcns (unit, chan)
int             unit;
int             chan;
{
    register int    i, maxlcn;
    struct dda_cb  *dc;

    maxlcn = nddach[unit];
    dc = &dda_softc[unit].dda_cb[1];
    for (i = 1; i <= maxlcn; i++, dc++)
	if ((dc->dc_flags & DC_RAW) && dc->dc_line == chan)
	{
	    abort_io (unit, dc->dc_lcn);
	    dc->dc_state = LC_IDLE;
	    dc->dc_line = -1;
	    dc->dc_inaddr.s_addr = 0;	/* forget address */
	    dc->dc_key.key_addr.s_addr = 0;
	    dc->dc_wsizein = dc->dc_wsizeout = 0;
	    dc->dc_pktsizein = dc->dc_pktsizeout = 0;
	    dc->dc_flags = 0;
	}
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                   PI_QUEUE_DATA()                           %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This routine queue up supervisory and data messages.  This */
/*	messages will be dequeued when the user does a read using  */
/*	the XIOREAD ioctl.  If the user tried to do a read and no  */
/*	data was ready then the process sleeps on pi_info[chan]pi_q*/
/*      This routine wakes up any of the sleeping processes.       */
/*                                                                 */
/*  Call:           pi_queue_data( unit, chan, lcn, cc, subcc, mb) */
/*  Argument:       unit: unit number (board)                      */
/*                  chan: channel for the data to be queue on      */
/*		    lcn: the lcn that the data came in on          */
/*		    cc: the read completion status from the mailbox*/
/*		    subcc: the read completion substatus           */
/*		    mb: pointer to the mbuf (or mbuf chain) that   */
/*		        contains the data packet.                  */
/*  Returns:        0 for success, else nonzero error code         */
/*  Called by:      pi_data(), pi_supr().                          */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_queue_data (unit, chan, lcn, cc, subcc, mb)
int             unit;
int             chan;
int             lcn;
int             cc;
int             subcc;
struct mbuf    *mb;
{
    int             s;
    struct pi_info *pi;
    struct pi_qmem *pq;

    if (chan == -1) {		/* invalid channel */
	DMESG (unit, 68, 
	    (DDALOG(LOG_ERR)
		"dda%d:(pi) data queue on dead channel: lcn %d\n",
		unit, lcn
	    DDAELOG));
	return;			/* don't do it, otherwise panic! */
    }

    pi = &pi_info[chan];
    s = splimp ();
    if (pi->pi_qlen == PIQLEN)	/* overflow */
    {
	DMESG(unit, 68,
	    (DDALOG(LOG_ERR)
		"dda%d:(pi) Data queue full, message dropped  chan %d lcn %d\n",
		 unit, chan, lcn
	    DDAELOG));
	m_freem (mb);
	splx (s);
	return;
    }
    else
	pi->pi_qlen++;
    if (pi->lastq == (PIQLEN - 1))
	pi->lastq = 0;
    else
	pi->lastq++;
    pq = &pi->pi_q[pi->lastq];
    pq->mb = mb;
    pq->lcn = lcn;
    pq->stat = cc;
    pq->substat = subcc;
    splx (s);
#ifdef DDA_MSGQ
    dda_mqstr ("(r wa) ");
#endif
    wakeup (pi->pi_q);		/* wakeup any sleepers */
    if (pi->signal)
	gsignal (pi->pgrp, pi->signal);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                     PI_REM_QDATA()                          %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	Remove a data item from the input queue and copy the       */
/*	information to the user pi_dblock structure that was       */
/*	passed to the XIOREAD case in piioctl.                     */
/*                                                                 */
/*  Call:           pi_rem_qdata(chan, dblk)                       */
/*  Argument:       chan: index of the channel to read.            */
/*                  dblk: pointer to user pi_dblock.               */
/*  Returns:        -1 if the input queue is empty.                */
/*                  0 if pi_dblock was filled in successfully.     */
/*		    error code if there was an error filling in    */
/*		       the pi_dblock.				   */
/*  Called by:      piioctl()                                      */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
pi_rem_qdata (chan, dblk)
int             chan;
struct pi_dblock *dblk;
{
    int             s, cnt = 0;
    struct pi_info *pi;
    struct pi_qmem *pq;
    struct mbuf    *mb, *m;
    caddr_t         usrdata;
    int             error = 0;

    pi = &pi_info[chan];
    s = splimp ();
    if (pi->pi_qlen == 0)
	return (-1);
    else
	pi->pi_qlen--;
    if (pi->firstq == (PIQLEN - 1))
	pi->firstq = 0;
    else
	pi->firstq++;
    pq = &pi->pi_q[pi->firstq];
    mb = pq->mb;
    dblk->func = pq->stat;
    dblk->subfunc = pq->substat;
    dblk->lcn = pq->lcn;
    bzero ((char *) pq, sizeof (*pq));	/* zero the structure out */
    splx (s);
    for (m = mb, usrdata = dblk->dataptr; m; m = m->m_next)
    {
	if (dblk->length < cnt + m->m_len)
	{
	    error = EINVAL;
	    goto out;
	}
	if (copyout (mtod (m, char *), usrdata, m->m_len))
	{
	    error = EFAULT;
	    goto out;
	}
	cnt += m->m_len;
	usrdata += m->m_len;
    }
out:
    dblk->length = cnt;
    m_freem (mb);
    return (error);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%               PI_FIND_CHAN_FOR_RING()                       %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This routine picks a channel to handle a ring.  It uses    */
/*	a static int (lastchan) to remember where it left off      */
/*	during the last search.  This will prevent it from always  */
/*	picking the same channel if there is more than 1 channel   */
/*	willing to accept the call.  If no channel is willing to   */
/*	accept the protocol type then it will go to a channel that */
/*	has indicated that it will accept any protocol type.       */
/* 	Note that the ring will not be given to a channel that     */
/*	has a full input queue.					   */
/*      This routine should be called a interrupt level.           */
/*                                                                 */
/*  Call:           pi_find_chan_for_ring (p)                      */
/*  Argument:       p: pointer to data containg the ring packet.   */
/*  Returns:        index of a channel willing to accept the call. */
/*                  -1 is return if no channel can accept          */
/*  Called by:      pi_supr()                                      */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
pi_find_chan_for_ring (p)
u_char          p[];		/* p is a pointer to a ring packet */

{
    register u_char *cp;
    int             i, proto, anychan = -1;
    static int      lastchan = 0;
    struct pi_info *pi;

    cp = p + 4;			/* skip over code, lcn, vcn and count in
				 * answer message */
    /* cp now points to length of called address */
    cp += *cp + 1;		/* skip over called address and length byte */
    /* cp now points to length of calling address */
    cp += *cp + 1;		/* skip over calling address and length byte */
    /* cp now points to length of protocol */
    if (*cp++ == 0)
	return (-1);
    proto = *cp;
    i = lastchan;
    do
    {
	i = (i + 1) % NPILINES;
	pi = &pi_info[i];
	if (pi->flags & PI_ACCEPT_RING)
	{
	    if (proto >= pi->protocols[0] && proto <= pi->protocols[1] && pi->pi_qlen < PIQLEN)
	    {
		lastchan = i;
		return (i);
	    }
	}
	if ((pi->flags & PI_ACCEPT_ANY) && anychan == -1 && pi->pi_qlen < PIQLEN)
	    anychan = i;
    } while (i != lastchan);
    if (anychan >= 0)
	lastchan = anychan;
    return (anychan);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%          PI_CIRCUIT_TO_HANDLE_PROTOCOL()                    %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This routine find out if there is a pi channel that is     */
/*	willing to accept a particular protocol.  This is needed   */
/*	because the PI interface has precedence over IP and X.29   */
/*	interfaces.  This routine is called before the data is     */
/*	passed to one of the interfaces.			   */
/*                                                                 */
/*  Call:           pi_circuit_to_handle_protocol(proto)           */
/*  Argument:       proto: protocol byte to check.                 */
/*  Returns:        1 if PI interface will handle the call.        */
/*		    0 otherwise.				   */
/*  Called by:      dda_decode_type().                             */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
pi_circuit_to_handle_protocol (proto)
u_char          proto;
{
    register int    i;
    struct pi_info *pi;

    for (i = 0, pi = pi_info; i < NPILINES; i++, pi++)
    {
	if (pi->flags & PI_ACCEPT_RING)
	    if (proto >= pi->protocols[0] && proto <= pi->protocols[1])
		return (1);
    }
    return (0);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      PI_WRITE()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This routine copies user data to an mbuf and queue the     */
/*	mbuf to go out.  The process will sleep if the output      */
/*	queue is full.   If the non-block bit is set then the      */
/*	process will not sleep and EWOULDBLOCK is returned.        */
/*      The last  byte of the built in data area for the mbuf      */
/*      is used to store the subfunction byte.  This byte is then  */
/*	put in the comregs by start_chn when it sees that this is  */
/*	a write for a RAW circuit.                                 */
/*								   */
/*  Call:           pi_write (ds, l, dblk)                         */
/*  Argument:       ds : pointer to dda_softc structure            */
/*                  l  : the channel (minor device) number         */
/*		    dblk : pointer to the pi_dblock for user data  */
/*  Returns:        0 for success, else nonzero error code         */
/*  Called by:      piioctl.                                       */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
pi_write (ds, l, dblk)
struct dda_softc *ds;
int             l;
struct pi_dblock *dblk;
{
    register int    length;
    struct dda_cb  *dc;
    struct mbuf    *m;
    char           *cp;
    struct ifqueue *oq;
    int             s, unit;

    length = dblk->length;
    unit = ds->dda_if.if_unit;
    if (length <= 0 || length > CLBYTES)
    {
	DMESG(unit, 69,
	    (DDALOG(LOG_ERR) "XIOWRITE: invalid length %d.\n", length DDAELOG));
	return (EINVAL);
    }
    m = 0;
    MGET (m, M_WAIT, MT_DATA);
    if (m == 0)
    {
	DMESG(unit, 70,
	    (DDALOG(LOG_ERR) "XIOWRITE: could not get small mbuf\n" DDAELOG));
	return (ENOBUFS);
    }
    if (length > (MLEN - 1))
    {
#if ACC_ULTRIX > 12
	struct mbuf    *p;
	MCLGET (m, p);
	if (p == 0)
#else
	MCLGET (m);
	if (m->m_len != CLBYTES)
#endif
	{
	    DMESG(unit, 71,
		(DDALOG(LOG_ERR)
		    "XIOWRITE: could not get page cluster\n"
		DDAELOG));
	    m_freem (m);
	    return (ENOBUFS);
	}
    }
    cp = mtod (m, char *);
    if (copyin (dblk->dataptr, cp, length))
    {
	m_freem (m);
	return (EFAULT);
    }
    m->m_len = length;
    if (length < (MLEN - 1))
	m->m_dat[MLEN-1] = dblk->subfunc;  /* store subfunction in last byte */
    dc = &ds->dda_cb[dblk->lcn];
    oq = &(dc->dc_oq);		/* point to output queue */
    s = splimp ();
    while (IF_QFULL (oq))	/* if q full */
    {
	if (dblk->flags & DB_NONBLOCK)
	{
	    m_freem (m);
	    splx (s);
	    return (EWOULDBLOCK);
	}
	else
	{
	    /*
	     * note that we save the mbuf here.  It may happen that the
	     * process dies in its sleep and we need a way to recover the
	     * allocated mbuf 
	     */
	    pi_info[l].msav = m;
#ifdef DDA_MSGQ
	    dda_mqstr ("(w sl) ");
#endif
	    sleep (&(dc->dc_oq), TTIPRI);
#ifdef DDA_MSGQ
	    dda_mqstr ("(w wo) ");
#endif
	}
    }
#ifdef DDA_MSGQ
    dda_mqstr ("(w go) ");
#endif
    IF_ENQUEUE (oq, m);		/* otherwise queue it */
    pi_info[l].msav = (struct mbuf *) 0;
    dda_start (ds, dc);		/* and try to output */
    dc->dc_timer = TMO_OFF;	/* reset timer */
    splx (s);
    return (0);
}

/*

Revision History:

13-Jul-89 PST	Modified LOCAL_VOIDs and statics to PRIVATE.

18-Jul-89 PST	Moved dc_key.ttyline out of union, creating dc_line.

26-Jul-89 PST	Replaced kernel printfs with DDALOG calls.

16-Aug-89 PST	Fixed DMESG macros.

29-Oct-89 PST	Fixed off-by-one errors where user could allocate but not
		use last LCN.  (Fix from brad@invector)

*/