OpenSS7
SS7 for the
Common Man

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified:

Home Overview Status News Documentation Resources About
   
 Overview
 Status
 News
 Documentation
 Resources
 About

   
Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/ua/ua.c


File /code/strss7/drivers/ua/ua.c



#ident "@(#) $RCSfile: ua.c,v $ $Name:  $($Revision: 0.8.2.3 $) $Date: 2003/04/03 19:51:46 $"

static char const ident[] =
    "$RCSfile: ua.c,v $ $Name:  $($Revision: 0.8.2.3 $) $Date: 2003/04/03 19:51:46 $";

#define __NO_VERSION__

#include <linux/config.h>
#include <linux/version.h>
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>

#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/cmn_err.h>

#include <ss7/ua_lm.h>
#include <ss7/ua_lm_ioctl.h>

#include "../lock.h"
#include "../debug.h"
#include "../bufq.h"

#include "ua.h"
#include "ua_data.h"

struct ua_driver *ua_driver = NULL;
struct ua_driver *m2ua_driver = NULL;
struct ua_driver *m3ua_driver = NULL;
struct ua_driver *isua_driver = NULL;
struct ua_driver *sua_driver = NULL;
struct ua_driver *tua_driver = NULL;

/*
 *  =========================================================================
 *
 *  Allocate, Deallocated and Cache Private structures.
 *
 *  =========================================================================
 */
/*
 *  Uppers
 *  -----------------------------------
 */
static int ua_free_upper(ua_driver_t * drv, dp_t * dp)
{
	int err;
	if (dp->ops && dp->ops->remove)
		if ((err = (*dp->ops->remove) (dp)))
			return (err);
	if ((*(dp->prev) = dp->next))
		dp->next->prev = dp->prev;
	dp->prev = NULL;
	dp->next = NULL;
	if (dp->bid)
		unbufcall(dp->bid);
	if (dp == drv->lm)
		drv->lm = NULL;
	dp->rq->q_ptr = NULL;
	dp->wq->q_ptr = NULL;
	kmem_cache_free(drv->cachep, dp);
	return (0);
}
static int ua_alloc_upper(ua_driver_t * drv, queue_t * q, dp_t ** dpp, dev_t * devp, uint type)
{
	int err;
	dp_t *dp;
	if (!(dp = kmem_cache_alloc(drv->cachep, SLAB_ATOMIC))) {
		bzero(dp, drv->psize);
		if ((dp->next = *dpp))
			dp->next->prev = &dp->next;
		dp->prev = dpp;
		*dpp = dp;
		dp->drv = drv;
		dp->id.dev = *devp;
		dp->type = type;
		dp->rq = RD(q);
		dp->wq = WR(q);
		dp->mq = RD(q);
		dp->ops = dp == drv->lm ? drv->lmq_ops : drv->ss7_ops;
		dp->rq->q_ptr = dp->wq->q_ptr = dp;
		if ((err = (*dp->ops->create) (dp))) {
			ua_free_upper(drv, dp);
			return (err);
		}
		return (0);
	}
	return (ENOMEM);
}
static dp_t *ua_find_upper(ua_driver_t * drv, dev_t dev)
{
	dp_t *dp;
	for (dp = drv->opens_list; dp && dp->id.dev != dev; dp = dp->next);
	return (dp);
}

/*
 *  Lowers
 *  -----------------------------------
 */
static int ua_free_lower(ua_driver_t * drv, lp_t * lp)
{
	int err;
	if (lp->ops && lp->ops->remove)
		if ((err = (*lp->ops->remove) (lp)))
			return (err);
	if ((*lp->prev) = lp->next)
		lp->prev->next = lp->prev;
	lp->prev = NULL;
	lp->next = NULL;
	if (lp->bid)
		unbufcall(lp->bid);
	lp->rq->q_ptr = NULL;
	lp->wq->q_ptr = NULL;
	kmem_cache_free(drv->cachep, lp);
	return (0);
}
static lp_t *lp;
ua_alloc_lower(ua_driver_t * drv, queue_t * q, dp_t * dp, linkblk * lb)
{
	lp_t *lp;
	if (!(lp = kmem_cache_alloc(drv->cachep, SLAB_ATOMIC))) {
		bzero(lp, drv->psize);
		if ((lp->next = drv->links_list))
			lp->next->prev = &lp->next;
		lp->prev = &drv->links_list;
		drv->links_list = lp;
		lp->drv = drv;
		lp->id.mux = lb->l_index;
		lp->rq = RD(lb->l_qbot);
		lp->wq = WR(lb->l_qbot);
		lp->mq = RD(q);
		lp->ops = NULL;
		lp->rq->q_ptr = lp->wq->q_ptr = lp;
	}
	return (lp);
}
static lp_t *ua_find_lower(ua_driver_t * drv, int mux)
{
	lp_t *lp;
	for (lp = drv->links_list; lp && lp->id.mux != mux; lp = lp->next);
	return (lp);
}

/*
 *  =========================================================================
 *
 *  M_IOCTL Processing
 *
 *  =========================================================================
 */
/*
 *  I_LINK, I_PLINK Handling
 *  -------------------------------------------------------------------------
 *  SS7 or SGP Provider or ASP User streams can be linked under the UA
 *  multiplexing driver.  We permit normal SS7 streams to perform an I_LINK of
 *  SS7 or SGP provider or ASP user streams under the UA driver, but we only
 *  permit the SS7 control stream to perform an I_PLINK of SS7 or SGP provider
 *  or ASP user streams.  This is so that if the configuration daemon crashes
 *  it can come back and the configuration has not been destroyed.
 *
 *  When linked, we generate the appropriate information request downstream to
 *  the newly linked SS7 or SGP provider or ASP user stream.  The purpose of
 *  this is to discover the response from the stream as to which state the
 *  stream is currently in and which addresses might be currently bound.  This
 *  permits the caller to bind the SS7 or SGP provider or ASP user streams.
 */
static inline int ua_i_link(queue_t * q, mblk_t * pdu, struct linkblk *lb)
{
	lp_t *lp;
	dp_t *dp = (dp_t *) q->q_ptr;
	queue_t *lq;
	ensure(lb, return (-EFAULT));
	lq = RD(lb->l_qbot);
	if (!(lp = kmalloc(sizeof(*lp), GFP_KERNEL)))
		return (-ENOMEM);
	bzero(lp, sizeof(*lp));
	lp->rq = RD(lq);
	lp->wq = WR(lq);
	lp->mq = RD(q);
	lp->id.mux = lb->l_index;
	lp->state = 0;
	RD(lq)->q_ptr = WR(lq)->q_ptr = lp;
	if ((lp->next = dp->links))
		lp->next->prev = &lp->next;
	lp->prev = &dp->links;
	noenable(lq);		/* disable queue till configured */
	return (lb->l_index);
}

/*
 *  I_UNLINK, I_PUNLINK Handling
 *  -------------------------------------------------------------------------
 *  When we unlink, if we still have referencing upper SS7-user streams, we
 *  send each of them a M_HANGUP message indicating the loss of the
 *  connection.  This might result in a SIG_PIPE signal being sent to the
 *  process if the SS7-user is a stream head.
 */
extern int m_flush_all(queue_t * q, mblk_t * mp, uint flag, uint band);
extern int m_hangup_all(queue_t * q, mblk_t * mp);
static inline int ua_i_unlink(queue_t * q, struct linkblk *lb)
{
	int err;
	lp_t *lp;
	dp_t *dp = (dp_t *) q->q_ptr;
	uint mux;
	ensure(lb, return (-EFAULT));
	mux = lb->l_index;
	for (lp = dp->links; lp && lp->id.mux != mux; lp = lp->next);
	if (!lp)
		return (-EINVAL);
	if ((err = m_flush_all(q, NULL, FLUSHW, 0)))
		return (err);
	if ((err = m_hangup_all(q, NULL)))
		return (err);
	if ((*(lp->prev) = lp->next))
		lp->next->prev = lp->prev;
	lp->rq->q_ptr = lp->wq->q_ptr = NULL;
	kfree(lp);
	return (0);
}
static int ua_w_ioctl(queue_t * q, mblk_t * pdu)
{
	int ret = -EINVAL;
	void *arg = pdu->b_cont ? pdu->b_cont->b_rptr : NULL;
	struct iocblk *iocp = (struct iocblk *) pdu->b_wptr;
	int cmd = iocp->ioc_cmd;
	switch (_IOC_TYPE(cmd)) {
	case __SID:
		switch (cmd) {
		case I_PLINK:
			if (q->q_ptr != ua_driver->lm) {
				ret = -EPERM;
				break;
			}
		case I_LINK:
			ret = ua_i_link(q, pdu, arg);
			break;
		case I_PUNLINK:
			if (q->q_ptr != ua_driver->lm) {
				ret = -EPERM;
				break;
			}
		case I_UNLINK:
			ret = ua_i_unlink(q, arg);
			break;
		}
		break;
	case UA_IOC_MAGIC:
	default:
		ret = -EOPNOTSUPP;
		break;
	}
	pdu->b_datap->db_type = ret < 0 ? M_IOCNAK : M_IOCACK;
	iocp->ioc_error = ret < 0 ? -ret : 0;
	iocp->ioc_rval = ret > 0 ? ret : -1;
	qreply(q, pdu);
	return (0);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  I_LINK, I_PLINK, I_UNLINK, I_PUNLINK Handling
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  I_LINK
 *  ----------------------------------------
 *  SS7 or SGP Provider or ASP User streams can be linked under the UA
 *  multiplexing driver.  We only permit normal SS7 streams to perform an
 *  I_LINK of SS7 or SGP provider or ASP user streams under the UA driver, and
 *  we only permit the SS7 control stream to perform an I_PLINK of SS7 or SGP
 *  provider or ASP user streams.  This is so that if the configuration daemon
 *  crashes it can come back and the configuration has not been destroyed.
 */
static int ua_i_link(queue_t * q, mblk_t * mp)
{
	lp_t *lp;
	dp_t *dp = (dp_t *) q->q_ptr;
	queue_t *lq;
	struct linkblk *lb;
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	ensure(mp->b_cont, return (-EFAULT));
	lb = (struct linkblk *) mp->b_cont->b_rptr;
	if (!(lp = ua_alloc_lower(drv, q, dp, lb)))
		return (-ENOMEM);
}

/*
 *  I_PLINK
 *  ----------------------------------------
 */
static int ua_i_plink(queue_t * q, mblk_t * mp)
{
	ua_t *m;
	ua_t *m = (ua_t *) q->q_ptr;
	struct linkblk *lb;
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	ensure(mp->b_cont, return (-EFAULT));
	lb = (struct linkblk *) mp->b_cont->b_rptr;
	/* only r00t ctrlq is allowed to make permanent links */
	if (!(m = ua_alloc_lower(lb)))
		return (-ENOMEM);
}

/*
 *  I_UNLINK
 *  ----------------------------------------
 */
static int ua_i_unlink(queue_t * q, mblk_t * mp)
{
	ua_t *m;
	ua_t *m = (ua_t *) q->q_ptr;
	struct linkblk *lb;
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	ensure(mp->b_cont, return (-EFAULT));
	lb = (struct linkblk *) mp->b_cont->b_rptr;
	if ((m = ua_find_lower(lb->l_index))) {
		if (m->mq == RD(lb->l_qtop)) {
			{
				fixme(("Send out of service if upper exists.\n"));
				m->rq->q_next = NULL;
				WR(m->mq) = NULL;
				m->mq = NULL;
				m->lq = NULL;
				m->uq = NULL;
				ua_free_lower(m);
				return (0);
			}
		}
		rare();
		return (-EPERM);
	}
	rare();
	return (-ENXIO);
}

/*
 *  I_PUNLINK
 *  ----------------------------------------
 */
static int ua_i_punlink(queue_t * q, mblk_t * mp)
{
	ua_t *m;
	struct linkblk *lb;
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	ensure(mp->b_cont, return (-EFAULT));
	/* only r00t ctrlq is allowed to undo permanent links */
	lb = (struct linkblk *) mp->b_cont->b_rptr;
	if ((m = ua_find_lower(lb->l_index))) {
		{
			fixme(("Send out of service if upper exists.\n"));
			ua_t *m = m->uq ? (ua_t *) m->uq->q_ptr : NULL;
			m->uq = NULL;
			m->lq = NULL;
			ua_free_lower(m);
			return (0);
		}
	}
	rare();
	return (-ENXIO);
}

/*
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 */

/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCDATA Handling
 *
 *  -------------------------------------------------------------------------
 */
static int ua_w_iocdata(queue_t * q, mblk_t * mp)
{
	ua_t *ua = (ua_t *) q->q_ptr;
	if (!ua->ops || !ua->ops->w_prim)
		return (4);
	return ((*ua->ops->w_prim) (q, mp));
}
static int ua_m_iocdata(queue_t * q, mblk_t * mp)
{
	ua_t *ua = (ua_t *) q->q_ptr;
	if (!ua->ops || !ua->ops->r_prim)
		return (4);
	return ((*ua->ops->r_prim) (q, mp));
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static int ua_reply_ioc(queue_t * q, mblk_t * mp, int err)
{
	struct iocblk *iocp = (struct iocblk *) mp->b_rptr;
	if (err < 0) {
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = -err;
		iocp->ioc_rval = -1;
	} else {
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval = 0;
	}
	qreply(q, mp);
	return (1);
}
static int ua_m_ioctl(queue_t * q, mblk_t * mp)
{
	int err = -EOPNOTSUPP;
	ua_t *ua = (ua_t *) q->q_ptr;
	struct iocblk *iocp = (struct iocblk *) mp->b_rptr;
	switch (_IOC_TYPE(iocp->ioc_cmd)) {
	case __SID:
		switch (iocp->ioc_cmd) {
		case I_PLINK:
			err = ua_i_plink(q, mp);
			break;
		case I_PUNLINK:
			err = ua_i_punlink(q, mp);
			break;
		case I_LINK:
		case I_UNLINK:
			err = -EPERM;
			break;
		}
		break;
	default:
		if (!ua->ops || !ua->ops->w_prim)
			return (4);
		err = (*ua->ops->r_prim) (q, mp);
		break;
	}
	return ua_reply_ioc(q, mp, err);
}
static int ua_p_ioctl(queue_t * q, mblk_t * mp)
{
	int err = -EOPNOTSUPP;
	ua_t *ua = (ua_t *) q->q_ptr;
	struct iocblk *iocp = (struct iocblk *) mp->b_rptr;
	struct linkblk *lb = (struct linkblk *) mp->b_cont->b_rptr;
	switch (_IOC_TYPE(iocp->ioc_cmd)) {
	case __SID:
		switch (iocp->ioc_cmd) {
		case I_LINK:
			err = ua_i_link(q, mp);
			break;
		case I_UNLINK:
			err = ua_i_unlink(q, mp);
			break;
		case I_PLINK:
		case I_PUNLINK:
			err = -EPERM;
			break;
		}
		break;
	default:
		if (!ua->ops || !ua->ops->w_prim)
			return (4);
		err = (*ua->ops->r_prim) (q, mp);
		break;
	}
	return ua_reply_ioc(q, mp, err);
}
static int ua_w_ioctl(queue_t * q, mblk_t * mp)
{
	ua_t *ua = (ua_t *) q->q_ptr;
	if (ua->type & UA_STRM_LM)
		return ua_m_ioctl(q, mp);
	else
		return ua_p_ioctl(q, mp);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
static int ua_m_flush(queue_t * q, mblk_t * mp, const uint8_t mflag, const uint8_t oflag);
{
	if (mp->b_rptr[0] & mflag) {
		if (mp->b_rptr[0] & FLUSHBAND)
			flushband(q, mp->b_rptr[1], FLUSHALL);
		else
			flushq(q, FLUSHALL);
		if (q->q_next) {
			putnext(q, mp);
			return (1);
		}
		mp->b_rptr[0] &= ~FLUSHW;
	}
	if (mp->b_rptr[0] & oflag && !(mp->b_flag & MSGNOLOOP)) {
		queue_t *oq = q->q_other;
		if (mp->b_rptr[0] & FLUSHBAND)
			flushband(oq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(oq, FLUSHALL);
		mp->b_flag |= MSGNOLOOP;
		qreply(q, mp);
		return (1);
	}
	return (0);
}
static int ua_w_flush(queue_t * q, mblk_t * mp)
{
	return ua_m_flush(q, mp, FLUSHW, FLUSHR);
}
static int ua_r_flush(queue_t * q, mblk_t * mp)
{
	return ua_m_flush(q, mp, FLUSHR, FLUSHW);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Other handling
 *
 *  -------------------------------------------------------------------------
 */
static int ua_w_other(queue_t * q, mblk_t * mp)
{
	ua_t *ua = (ua_t *) q->q_ptr;
	if (!ua->ops || !ua->ops->w_prim)
		return (4);
	return ((*ua->ops->w_prim) (q, mp));
}
static int ua_r_other(queue_t * q, mblk_t * mp)
{
	ua_t *ua = (ua_t *) q->q_ptr;
	if (!ua->ops || !ua->ops->r_prim)
		return (4);
	return ((*ua->ops->r_prim) (q, mp));
}

/*
 *  =========================================================================
 *
 *  STREAMS PUTQ and SRVQ routines
 *
 *  =========================================================================
 *
 *  UA WPUT
 *  -------------------------------------------------------------------------
 */
static INT ua_wput(queue_t q, mblk_t * mp)
{
	int rtn;
	ensure(q, return ((INT) (-EFAULT)));
	ensure(mp, return ((INT) (-EFAULT)));
	if (mp->b_datap->db_type < QPCTL && q->q_count) {
		seldom();
		putq(q, mp);
		return (INT) (0);
	}
	switch (mp->b_datap->db_type) {
	case M_IOCTL:
		rtn = ua_w_ioctl(q, mp);
		break;
	case M_IOCDATA:
		rtn = ua_w_iocdata(q, mp);
		break;
	case M_FLUSH:
		rtn = ua_w_flush(q, mp);
		break;
	default:
		rtn = ua_w_other(q, mp);
		break;
	}
	switch (rtn) {
	case 0:
		freemsg(mp);
	case 1:
		break;
	case 2:
		freeb(mp);
		break;
	case 3:
		if (!q->q_next) {
			qreply(q, mp);
			break;
		}
	case 4:
		if (q->q_next) {
			putnext(q, mp);
			break;
		}
		rtn = -EOPNOTSUPP;
		/* fall through */
	default:
		ptrace(("Dropping error = %d\n", rtn));
		freemsg(mp);
		return (INT) (rtn);
	case -ENOBUFS:		/* caller must schedule bufcall */
	case -EBUSY:		/* caller must have failed canput */
	case -EAGAIN:		/* caller must re-enable queue */
	case -ENOMEM:		/* caller must re-enable queue */
		putq(q, mp);
		return (INT) (0);
	}
	return (INT) (0);
}

/*
 *  UA WSRV
 *  -------------------------------------------------------------------------
 */
static INT ua_wsrv(queue_t * q)
{
	int rtn;
	mblk_t *mp;
	while ((mp = getq(q))) {
		switch (mp->b_datap->db_type) {
		case M_IOCTL:
			rtn = ua_w_ioctl(q, mp);
			break;
		case M_IOCDATA:
			rtn = ua_w_iocdata(q, mp);
			break;
		case M_FLUSH:
			rtn = ua_w_flush(q, mp);
			break;
		default:
			rtn = ua_w_other(q, mp);
			break;
		}
		switch (rtn) {
		case 0:
			freemsg(mp);
		case 1:
			continue;
		case 2:
			freeb(mp);
			continue;
		case 3:
			if (!q->q_next) {
				qreply(q, mp);
				continue;
			}
		case 4:
			if (q->q_next) {
				putnext(q, mp);
				continue;
			}
			rtn = -EOPNOTSUPP;
		default:
			ptrace(("Dropping error = %d\n", rtn));
			freemsg(mp);
			continue;
		case -ENOBUFS:	/* caller must schedule bufcall */
		case -EBUSY:	/* caller must have failed canput */
		case -EAGAIN:	/* caller must re-enable queue */
		case -ENOMEM:	/* caller must re-enable queue */
			if (mp->b_datap->db_type < QPCTL) {
				putbq(q, mp);
				return (INT) (0);
			}
			ptrace(("Dropping error = %d\n", rtn));
			freemsg(mp);
			continue;
		}
	}
	return (INT) (0);
}

/*
 *  UA RPUT
 *  -------------------------------------------------------------------------
 */
static INT ua_rput(queue_t q, mblk_t * mp)
{
	int rtn;
	ensure(q, return ((INT) (-EFAULT)));
	ensure(mp, return ((INT) (-EFAULT)));
	if (mp->b_datap->db_type < QPCTL && q->q_count) {
		seldom();
		putq(q, mp);
		return (INT) (0);
	}
	switch (mp->b_datap->db_type) {
	case M_FLUSH:
		rtn = ua_r_flush(q, mp);
		break;
	default:
		rtn = ua_r_other(q, mp);
		break;
	}
	switch (rtn) {
	case 0:
		freemsg(mp);
	case 1:
		break;
	case 2:
		freeb(mp);
		break;
	case 3:
		if (!q->q_next) {
			qreply(q, mp);
			break;
		}
	case 4:
		if (q->q_next) {
			putnext(q, mp);
			break;
		}
		rtn = -EOPNOTSUPP;
		/* fall through */
	default:
		ptrace(("Dropping error = %d\n", rtn));
		freemsg(mp);
		return (INT) (rtn);
	case -ENOBUFS:		/* caller must schedule bufcall */
	case -EBUSY:		/* caller must have failed canput */
	case -EAGAIN:		/* caller must re-enable queue */
	case -ENOMEM:		/* caller must re-enable queue */
		putq(q, mp);
		return (INT) (0);
	}
	return (INT) (0);
}

/*
 *  UA RSRV
 *  -------------------------------------------------------------------------
 */
static INT ua_rsrv(queue_t * q)
{
	int rtn;
	mblk_t *mp;
	while ((mp = getq(q))) {
		switch (mp->b_datap->db_type) {
		case M_FLUSH:
			rtn = ua_r_flush(q, mp);
			break;
		default:
			rtn = ua_r_other(q, mp);
			break;
		}
		switch (rtn) {
		case 0:
			freemsg(mp);
		case 1:
			continue;
		case 2:
			freeb(mp);
			continue;
		case 3:
			if (!q->q_next) {
				qreply(q, mp);
				continue;
			}
		case 4:
			if (q->q_next) {
				putnext(q, mp);
				continue;
			}
			rtn = -EOPNOTSUPP;
		default:
			ptrace(("Dropping error = %d\n", rtn));
			freemsg(mp);
			continue;
		case -ENOBUFS:	/* caller must schedule bufcall */
		case -EBUSY:	/* caller must have failed canput */
		case -EAGAIN:	/* caller must re-enable queue */
		case -ENOMEM:	/* caller must re-enable queue */
			if (mp->b_datap->db_type < QPCTL) {
				putbq(q, mp);
				return (INT) (0);
			}
			ptrace(("Dropping error = %d\n", rtn));
			freemsg(mp);
			continue;
		}
	}
	return (INT) (0);
}

/*
 *  BUFSRV calls service routine
 *  -------------------------------------------------------------------------
 */
static void ua_bufsrv(long data)
{
	ua_t *ua = (ua_t *) data;
	if (ua->bid) {
		ua->bid = 0;
		qenable(ua->rq);
		qenable(ua->wq);
	}
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 *
 *  OPEN
 *  ------------------------------
 */
int ua_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	int err;
	int type = 0;
	int cmajor = getmajor(*devp);
	int cminor = getminor(*devp);
	dp_t **dpp;
	ua_driver_t *drv;
	switch (cmajor) {
	case UA_CMAJOR:
		drv = ua_driver;
		type |= UA_PROT_NONE;
		break;
	case M2UA_CMAJOR:
		drv = m2ua_driver;
		type |= UA_PROT_SL;
		break;
	case M3UA_CMAJOR:
		drv = m3ua_driver;
		type |= UA_PROT_MTP;
		break;
	case ISUA_CMAJOR:
		drv = isua_driver;
		type |= -UA_PROT_ISUP;
		break;
	case SUA_CMAJOR:
		drv = sua_driver;
		type |= UA_PROT_SCCP;
		break;
	case TUA_CMAJOR:
		drv = tua_driver;
		type |= UA_PROT_TCAP;
		break;
	default:
		drv = NULL;
		break;
	}
	/* 
	 *  FIXME:
	 *      Later we might want to consider demand loading our own
	 *      modules. (LiS-2.13 does not support demand loading of
	 *      drivers.)  What we would do is link a small demand loader
	 *      with LiS STREAMS and have it register a bunch of majors
	 *      and then have it call.
	 */
	if (!drv || cmajor != drv->cmajor)
		return (EIO);
	if (q->q_ptr != NULL)
		return (0);	/* already open */
	if (sflag == MODOPEN || WR(q)->q_next)
		return (EIO);	/* cannot push as module */
	if (cminor == drv->nminor) {
		if (drv->lm)
			return (ENXIO);	/* only one */
		if (crp->cr_uid != 0)
			return (EPERM);	/* need r00t */
		dpp = &drv->lm;
		type |= UA_STRM_LM;
	} else {
		if (!cminor)
			sflag = CLONEOPEN;
		if (sflag == CLONEOPEN)
			cminor = 1;
		dpp = &drv->opens_list;
		for (; *dpp && getmajor((*dpp)->id.dev) < cmajor; dpp = &(*dpp)->next);
		for (; *dpp && cminor < drv->nminor; dpp = &(*dpp)->next) {
			int dminor = getminor((*dpp)->id.dev);
			if (cminor < dminor)
				break;
			if (cminor == dminor) {
				if (sflag == CLONEOPEN) {
					cminor++;
					continue;
				}
				rare();
				return (EIO);
			}
		}
		if (cminor > drv->nminor)
			return (ENXIO);
		type |= UA_STRM_SS7;
	}
	*devp = makedevice(cmajor, cminor);
	if ((err = ua_alloc_upper(drv, q, dpp, devp, type)))
		return (err);
	return (0);
}

/*
 *  CLOSE
 *  ------------------------------
 */
int ua_close(queue_t * q, int flag, cred_t * crp)
{
	dp_t *dp = (dp_t *) q->q_ptr;
	if (!dp)
		return (EIO);
	return ua_free_upper(dp->drv, dp);
}


Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/ua/ua.c

OpenSS7
SS7 for the
Common Man
Home Overview Status News Documentation Resources About

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified: