/*****************************************************************************
 * Copyright (c) 1988-95 Digi International Inc., All Rights Reserved
 * 
 * This  software  contains  proprietary  and  confidential   information   of
 * Digi International  Inc.   By  accepting  transfer  of this copy, Recipient
 * agrees to retain this software in  confidence,  to  prevent  disclosure  to
 * others,  and  to make no  use of this software other than that for which it
 * was delivered.  This is an unpublished copyright work  Digi   International
 * Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is
 * strictly prohibited.
 * 
 * Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc.
 * Confidential  when   combined   with   the   aggregated  modules  for  this
 * product) OBJECT CODE ONLY SOURCE MATERIALS
 * 
 * (C) COPYRIGHT Digi International Inc. 1995
 * 
 * All Rights Reserved
 * 
 * US   Government   Users   Restricted   Rights   -   Use,   duplication   or
 * disclosure  restricted by GSA ADP Schedule Contract with Digi International
 * Inc.
 * 
 *****************************************************************************/

/******************************************************************
 *
 *  Embedded STREAMS Package
 *
 *  Copyright 1990  Spider Systems Limited
 *
 *  STREAM.C
 *
 *    Stream management
 *
 ******************************************************************/

/*
 * $Id: stream.c,v 1.5 1997/10/22 21:52:24 gary Exp $
 */


#include "types.h"
#include "stream.h"
#include "queue.h"
#if SOH
#include "sohalloc.h"
#endif
#include "errno.h"
#include "cnt_arry.h"

extern struct streamtab *cdevs[];
extern struct streamtab *fmods[];
extern mblk_t *allocb() ;

int link_index = 1;

static int linkreply();
static ioc_error;

static struct module_info hrinfo = { 0, "head R", 0, 0, 0, 0 };
static struct module_info hwinfo = { 0, "head W", 0, 0, 0, 0 };
static struct qinit hrinit = { linkreply, 0, 0, 0, 0, &hrinfo, 0 };
static struct qinit hwinit = { 0, 0, 0, 0, 0, &hwinfo, 0 };

void spop();

/************************/
/*	news		*/
/************************/
strm_t *news(rinit, winit)
struct qinit *rinit, *winit;
{
    strm_t *s;
    queue_t *q;

    s = allocs();
    q = allocq();

    if (s && q)
    {   /* Got resources OK */
	s->str_sq = q;
	s->str_clast = s;
	initq(q, (rinit ? rinit : &hrinit), (winit ? winit : &hwinit));

	/* Success */
	return s;
    }
    if (s) frees(s);
    return (strm_t *)0;
}

/************************/
/*	sopen		*/
/************************/
int sopen(s, dev, flag, sflag)
strm_t *s;
unsigned int dev, flag, sflag;
{
    struct streamtab *stab;
    queue_t *dq, *q;
    int m, r;

    if (s->str_dq)
    {   /* Already open, so just open modules and driver again */
	for (dq = s->str_sq->q_next;
	     dq && (*RD(dq)->q_qinfo->qi_qopen)(RD(dq),
						dev,
						flag,
						dq->q_next ? MODOPEN : 0) >= 0;
	     dq = dq->q_next);

	/* If dq not NULL, one of the opens failed */
	if (dq)
	    return OPENFAIL;

	/* Success */
	return 0;
    }

    /* Find major device number */
    if ((m = major(dev))==CLONEDEV)
    {
	sflag = CLONEOPEN;
	m = minor(dev);
	dev = makedev((uint)m, 0);
    }

    /* Find and check streamtab entry */
    stab = cdevs[m];
    if (stab==(struct streamtab *)0)
    {   /* unable to find major device */
	return ENODEV;
    }

    /* Fill in stream structure */
    s->str_dev = dev;
    s->str_stab = stab;

    /* Get driver queues */
    if (dq = allocq())
    {   /* Got queues OK: link it all up */
	s->str_dq = dq;
	s->str_dev = dev;
	s->str_stab = stab;

	/* Attach driver to head */
	initq(dq, stab->st_rdinit, stab->st_wrinit);
	linkq(s->str_sq, dq);

	/* Now open the driver */
	if (q = RD(dq), (r = (*q->q_qinfo->qi_qopen)(q, dev, flag, sflag))>=0)
	{   /* Opened OK */

	    /* Deal with clone minor device */
	    if (sflag==CLONEOPEN)
		s->str_dev = makedev((uint)m, (minor(r)));

	    /* Success */
	    return 0;
	}
    }
    else
	r = ENXIO;

    return r;
}

static void close_qs(q)
queue_t *q;
{    /* Close stream below queue 'q' */
    queue_t *xq = q;

    /* Close all lower queues on chain */
    while (q = q->q_next)
    {
	cnt_arry[STAGE] = 499 ;
	(*RD(q)->q_qinfo->qi_qclose)(RD(q),0);
	cnt_arry[STAGE] = 503 ;
	cnt_arry[LOWER_QUEUE_CNT]++ ;
    }

    /* Free all queues (avoiding loss of pointer in freeq) */
    do
    {
	cnt_arry[STAGE] = 504 ;
	q = xq->q_next;
	freeq(xq);
	cnt_arry[STAGE] = 505 ;
	cnt_arry[FREE_QUEUES]++ ;
    }
    while (xq = q);
}


/************************/
/*	sclose		*/
/************************/
void sclose(s)
strm_t *s;
{   /* Close stream s */

    strm_t *hs, *xs;

    /* Check if already closed */
    if (s->str_dq)
    {   /* Open: so close stream itself */
	close_qs(s->str_sq);

	/* Now all controlled streams */
	for (hs = s->str_cnext; xs = hs; )
	{
	    cnt_arry[STAGE] = 506 ; 
	    close_qs(hs->str_sq);
	    cnt_arry[STAGE] = 507 ; 
/*	    freeq(hs->str_sq);	*/
	    cnt_arry[STAGE] = 508 ;
	    hs = hs->str_cnext;
	    frees(xs);
	    cnt_arry[STAGE] = 509 ;
	}

	/* Tidy up stream head */
	s->str_dq = (queue_t *)0;
	s->str_cnext = (strm_t *)0;
	s->str_clast = s;
	s->str_stab = (struct streamtab *)0;
	cnt_arry[STAGE] = 510 ;
    } 
}

/************************/
/*	spush		*/
/************************/
int spush(s, m)
strm_t *s;
int m;
{
    struct streamtab *stab;
    queue_t *mq;
    int r;

    /* Find and check streamtab entry */
    /* for (r = 0; (stab = fmods[r]) && r!=m; r++); */
    stab = fmods[m];
    if (stab==(struct streamtab *)0)
	return ENODEV;

    /* Get queues for module */
    if ((mq = allocq())==(queue_t *)0)
	return ENOSR;

    /* Initialise queues and link below stream head */
    initq(mq, stab->st_rdinit, stab->st_wrinit);
    linkq(s->str_sq, mq);

    /* Now open the module */
    if ((*RD(mq)->q_qinfo->qi_qopen)(RD(mq), 0, 0, MODOPEN)<0)
    {   /* Open failed */

	printf("push of module failed") ;
    	/* Unlink and free module QUEUE pair */
/*   	 unlinkq(mq); */
  /*  	freeq(mq); */

/*	spop(s);  */
	return EINVAL;
    }
    return 0;
}


/************************/
/*	spop		*/
/************************/
void spop(s)
strm_t *s;
{
    queue_t *mq;

    printf("spop s = %x", s ) ;
    /* Call close function of module to be popped */
    mq = s->str_sq->q_link;
    printf("spop mq = %x", mq ) ;
    (*RD(mq)->q_qinfo->qi_qclose)(RD(mq),0);

    /* Unlink and free module QUEUE pair */
    unlinkq(mq);
    freeq(mq);
}


static mblk_t *getlkmsg(cmd, qtop, qbot, index)
int cmd;
queue_t *qtop, *qbot;
{
    mblk_t *mp, *bp;
    bp = allocb(sizeof(struct linkblk), BPRI_MED);
    mp = allocb(sizeof(struct iocblk), BPRI_MED);
    if (mp && bp)
    {   /* Can make up message */

	{  /* Build iocblk */
	    register struct iocblk FAR *iocp = (struct iocblk FAR *)mp->b_wptr;
	    mp->b_wptr += sizeof(struct iocblk);
	    bzero((bufp_t)iocp, sizeof(struct iocblk));
	    iocp->ioc_cmd = cmd;
	    iocp->ioc_id = index;
	    iocp->ioc_count = sizeof(struct linkblk);
	    mp->b_datap->db_type = M_IOCTL;
	}

	{   /* Build linkblk */
	    struct linkblk FAR *linkp = (struct linkblk FAR *)bp->b_wptr;
	    bp->b_wptr += sizeof(struct linkblk);
	    linkp->l_qtop = qtop;
	    linkp->l_qbot = qbot;
	    linkp->l_index = index;
	}

	/* Success */
	mp->b_cont = bp;
	return mp;
    }

    /* Failed */
    if (mp) freeb(mp);
    if (bp) freeb(bp);
    return (mblk_t *)0;
}

/************************/
/*	slink		*/
/************************/
int slink(cs, ls)
strm_t *cs, *ls;
{
    mblk_t *mp;
    queue_t *lq;

    if (mp = getlkmsg(I_LINK, cs->str_dq, ls->str_sq, link_index))
    {   /* We have a link message */

	/* Fill in link details */
	ls->str_lindex = link_index;
	ls->str_cstrm = cs;

	/* Step index for next link */
	if (++link_index<=0) link_index = 3;

	/* Set stream head up as lower MUX queue */
	initq(lq = ls->str_sq,
	      cs->str_stab->st_muxrinit,
	      cs->str_stab->st_muxwinit);

	/* Add ls to cs's controlled stream list */
	cs->str_clast->str_cnext = ls;
	ls->str_cprev = cs->str_clast;
	cs->str_clast = ls->str_clast;

	/* Send I_LINK ioctl message */
	(*cs->str_dq->q_qinfo->qi_putp)(cs->str_dq, mp);

	/* Result */
	if (!ioc_error) return ls->str_lindex;
    }

    /* Failed */
    return 0;
}

/************************/
/*	linkreply	*/
/************************/
static int linkreply(q, mp)
queue_t *q;
mblk_t *mp;
{
    struct iocblk FAR *iocb = (struct iocblk FAR *)mp->b_rptr;

    switch (mp->b_datap->db_type)
    {
    case M_IOCACK:
	ioc_error = iocb->ioc_error;
	break;

    case M_IOCNAK:
	if (!(ioc_error = iocb->ioc_error))
	    ioc_error = EINVAL;
	break;

    default:
	break;
    }

    freemsg(mp);

	return 1;
}

/************************/
/*	sunlink		*/
/************************/
strm_t *sunlink(cs, index)
strm_t *cs;
int index;
{
    strm_t *ls;
    mblk_t *mp;
    queue_t *qtop;

    /* Look for right link block */
    for (ls = cs->str_cnext;
	 ls && ls->str_lindex!=index;
	 ls = ls->str_cnext);

    if (ls && (mp = getlkmsg(I_UNLINK,
			     qtop = ls->str_cstrm->str_dq,
			     ls->str_sq,
			     index)))
    {   /* Found it, and have got unlink message OK */
	(*qtop->q_qinfo->qi_putp)(qtop, mp);

	/* Now unhitch it (with any lower controlled streams) */
	ls->str_clast->str_cnext->str_cprev = ls->str_cprev;
	if ((ls->str_cprev->str_cnext = ls->str_clast->str_cnext) ==
								(strm_t *)0)
	    cs->str_clast = ls->str_cprev;
	ls->str_cprev = ls->str_cstrm = ls->str_clast->str_cnext =
								(strm_t *)0;
	/* Success */
	return ls;
    }

    /* Failed */
    return (strm_t *)0;
}
