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/tcap/tcap.c


File /code/strss7/drivers/tcap/tcap.c



#ident "@(#) $RCSfile: tcap.c,v $ $Name:  $($Revision: 0.8.2.3 $) $Date: 2003/04/14 12:13:47 $"

static char const ident[] =
    "$RCSfile: tcap.c,v $ $Name:  $($Revision: 0.8.2.3 $) $Date: 2003/04/14 12:13:47 $";

/*
 *  This is a TCAP (Transaction Capabilities Application Part) mulitplexing
 *  driver for SS7.  It is opened and SCCP streams are I_LINKed under the
 *  driver by the TCAP User or I_PLINKed under the driver by the TCAP control
 *  stream for use by TCAP Users.  TCAP Users are associated with lower SCCP
 *  streams and TCAP application contexts via the TCAP bind commands.
 *
 *  This includes a management stream for module reporting and configuration
 *  management.
 */

#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 "../debug.h"
#include "tcap.h"
#include "tcap_data.h"
#include "../sccpi/sccp_user.h"

#define TCAP_DESCRIP	"TCAP STREAMS MULTIPLEXING DRIVER."
#define TCAP_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  All Rights Reserved."
#define TCAP_DEVICE	"Part of the OpenSS7 Stack for LiS STREAMS."
#define TCAP_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define TCAP_LICENSE	"GPL"
#define TCAP_BANNER	TCAP_DESCRIP	"\n" \
			TCAP_COPYRIGHT	"\n" \
			TCAP_DEVICE	"\n" \
			TCAP_CONTACT	"\n"

#ifdef MODULE
MODULE_AUTHOR(TCAP_CONTACT);
MODULE_DESCRIPTION(TCAP_DESCRIP);
MODULE_SUPPORTED_DEVICE(TCAP_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(TCAP_LICENSE);
#endif
#define MODULE_STATIC static
#else
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#define MODULE_STATIC
#endif

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */

static struct module_info tcap_minfo = {
	TCAP_MODULE_ID,			/* Module ID number */
	"tcap",				/* Module name */
	1,				/* Min packet size accepted *//* XXX */
	254,				/* Max packet size accepted *//* XXX */
	8 * 512,			/* Hi water mark *//* XXX */
	1 * 512				/* Lo water mark *//* XXX */
};

static int tcap_open(queue_t *, dev_t *, int, int, cred_t *);
static int tcap_close(queue_t *, int, cred_t *);

static INT tcap_rput(queue_t *, mblk_t *);
static INT tcap_rsrv(queue_t *);
static INT tcap_wput(queue_t *, mblk_t *);
static INT tcap_wsrv(queue_t *);

static struct qinit tcap_u_rinit = {
	tcap_rput,			/* Read put (msg from below) */
	tcap_rsrv,			/* Read queue service */
	tcap_open,			/* Each open */
	tcap_close,			/* Last close */
	NULL,				/* Admin (not used) */
	&tcap_minfo,			/* Information */
	NULL				/* Statistics */
};
static struct qinit tcap_u_winit = {
	tcap_wput,			/* Write put (msg from above) */
	tcap_wsrv,			/* Write queue service */
	NULL,				/* Each open */
	NULL,				/* Last close */
	NULL,				/* Admin (not used) */
	&tcap_minfo,			/* Information */
	NULL				/* Statistics */
};

static struct qinit tcap_l_winit = {
	tcap_wput,			/* Write put (msg from above) */
	tcap_wsrv,			/* Write queue service */
	NULL,				/* Each open */
	NULL,				/* Last close */
	NULL,				/* Admin (not used) */
	&tcap_minfo,			/* Information */
	NULL				/* Statistics */
};
static struct qinit tcap_l_rinit = {
	tcap_rput,			/* Read put (msg from above) */
	tcap_rsrv,			/* Read queue service */
	NULL,				/* Each open */
	NULL,				/* Last close */
	NULL,				/* Admin (not used) */
	&tcap_minfo,			/* Information */
	NULL				/* Statistics */
};

MODULE_STATIC struct streamtab tcap_info = {
	&tcap_u_rinit,			/* Upper read queue */
	&tcap_u_winit,			/* Upper write queue */
	&tcap_l_rinit,			/* Lower read queue */
	&tcap_l_winit			/* Lower write queue */
};

/*
 *  =========================================================================
 *
 *  Buffer Allocation
 *
 *  =========================================================================
 */
/*
 *  BUFSRV calls service routine
 *  -----------------------------------
 *  Buffer call service routine, simply enables the queue against which the
 *  buffer call was orignally scheduled.
 */
static void tcap_bufsrv(long data)
{
	queue_t *q = (queue_t *) data;
	priv_t *pp = (priv_t *) q->q_ptr;
	if (q == RD(q))
		if (pp->rbid)
			pp->rbid = 0;
	if (q == WR(q))
		if (pp->wbid)
			pp->wbid = 0;
	qenable(q);
}

/*
 *  BUFCALL for enobufs
 *  -----------------------------------
 *  Generate a buffer call against a queue.
 */
int tcap_bufcall(queue_t * q, size_t size, int prior)
{
	priv_t *pp = (priv_t *) q->q_ptr;
	if (q == RD(q))
		if (!pp->rbid)
			pp->rbid = bufcall(size, prior, &tcap_bufsrv, (long) q);
	if (q == WR(q))
		if (!pp->wbid)
			pp->wbid = bufcall(size, prior, &tcap_bufsrv, (long) q);
	return (-ENOBUFS);
}

/*
 *  ALLOCB
 *  -----------------------------------
 */
mblk_t *tcap_allocb(queue_t * q, size_t size, int prior, int *errp)
{
	mblk_t *mp;
	if ((mp = allocb(size, prior)))
		return (mp);
	*errp = tcap_bufcall(q, size, prior);
	return (NULL);
}

/*
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 */
/*
 *  M_FLUSH Handling
 *  -----------------------------------
 */
static inline int tcap_m_flush(queue_t * q, mblk_t * mp, const uint8_t flag, const uint8_t oflag)
{
	if (mp->b_rptr[0] & flag) {
		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] &= ~flag;
	}
	if (mp->b_rptr[0] & oflag) {
		if (mp->b_rptr[0] & FLUSHBAND)
			flushband(OTHER(q), mp->b_rptr[1], FLUSHALL);
		else
			flushq(OTHER(q), FLUSHALL);
		if (OTHER(q)->q_next) {
			qreply(q, mp);
			return (1);
		}
	}
	return (0);
}

int tcap_w_flush(queue_t * q, mblk_t * mp)
{
	return tcap_m_flush(q, mp, FLUSHW, FLUSHR);
}

int tcap_r_flush(queue_t * q, mblk_t * mp)
{
	return tcap_m_flush(q, mp, FLUSHR, FLUSHW);
}

/*
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 *
 *  PUTQ Put Routine
 *  -----------------------------------
 */
static inline int tcap_putq(queue_t * q, mblk_t * mp, int (*proc) (queue_t *, mblk_t *))
{
	int rtn;
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	if (mp->b_datap->db_type < QPCTL && q->q_count) {
		seldom();
		putq(q, mp);
		return (0);
	}
	switch ((rtn = (*proc) (q, mp))) {
	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;
	default:
		ptrace(("ERROR: (dropping) %d\n", rtn));
		freemsg(mp);
		return (rtn);
	case 6:
		putq(q, mp);
		return (0);
	case 5:
		if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
			putnext(q, mp);
			break;
		}
	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);
		break;
	}
	return (0);
}

/*
 *  SRVQ Service Routing
 *  -----------------------------------
 */
static inline int tcap_srvq(queue_t * q, int (*proc) (queue_t *, mblk_t *))
{
	int rtn;
	mblk_t *mp;
	ensure(q, return (-EFAULT));
	while ((mp = getq(q))) {
		switch ((rtn = (*proc) (q, mp))) {
		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;
			}
		default:
			ptrace(("ERROR: (dropping) %d\n", rtn));
			freemsg(mp);
			continue;
		case 6:
			ptrace(("ERROR: Noenabling queue\n"));
			noenable(q);
			putbq(q, mp);
			return (0);
		case 5:
			if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
				putnext(q, mp);
				break;
			}
		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 */
			ptrace(("ERROR: Puting back, queue stalled\n"));
			if (mp->b_datap->db_type < QPCTL) {
				putbq(q, mp);
				return (rtn);
			}
			if (mp->b_datap->db_type == M_PCPROTO) {
				mp->b_datap->db_type = M_PROTO;
				mp->b_band = 255;
				putq(q, mp);
				break;
			}
			ptrace(("ERROR: (dropping) %d\n", rtn));
			freemsg(mp);
			continue;
		}
	}
	return (0);
}
static INT tcap_rput(queue_t * q, mblk_t * mp)
{
	priv_t *pp = (priv_t *) q->q_ptr;
	ensure(pp, return ((INT) (-EFAULT)));
	ensure(pp->ops.r_prim, return ((INT) (-EFAULT)));
	return (INT) tcap_putq(q, mp, pp->ops.r_prim);
}
static INT tcap_rsrv(queue_t * q)
{
	priv_t *pp = (priv_t *) q->q_ptr;
	ensure(pp, return ((INT) (-EFAULT)));
	ensure(pp->ops.r_prim, return ((INT) (-EFAULT)));
	return (INT) tcap_srvq(q, pp->ops.r_prim);
}
static INT tcap_wput(queue_t * q, mblk_t * mp)
{
	priv_t *pp = (priv_t *) q->q_ptr;
	ensure(pp, return ((INT) (-EFAULT)));
	ensure(pp->ops.w_prim, return ((INT) (-EFAULT)));
	return (INT) tcap_putq(q, mp, pp->ops.w_prim);
}
static INT tcap_wsrv(queue_t * q)
{
	priv_t *pp = (priv_t *) q->q_ptr;
	ensure(pp, return ((INT) (-EFAULT)));
	ensure(pp->ops.w_prim, return ((INT) (-EFAULT)));
	return (INT) tcap_srvq(q, pp->ops.w_prim);
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 */
static int tcap_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	tcap_t *tcap, **trp;
	int cmajor = getmajor(*devp);
	int cminor = getminor(*devp);
	if (sflag == MODOPEN || WR(q)->q_next) {
		ptrace(("ERROR: Can't push as module\n"));
		return (EIO);
	}
	if (cmajor == TCAP_CMAJOR && (cminor == 1 || cminor == 2)
	    && crp->cr_uid != 0) {
		ptrace(("ERROR: Can't open control stream without r00t permission\n"));
		return (EPERM);
	}
	if (q->q_ptr != NULL) {
		ptrace(("INFO: Device already open\n"));
		return (0);
	}
	if (sflag == CLONEOPEN) {
		ptrace(("INFO: Clone open in effect\n"));
		cmajor = TCAP_CMAJOR;
		cminor = 0;
	}
	if (cmajor == TCAP_CMAJOR && cminor == 0 && sflag != CLONEOPEN) {
		ptrace(("INFO: Clone minor opened\n"));
		sflag = CLONEOPEN;
		cminor = 3;
	}
	for (trp = &tcap_opens; *trp; trp = &(*trp)->next) {
		ushort dmajor = getmajor((*trp)->devid);
		if (cmajor < dmajor)
			break;
		if (cmajor == dmajor) {
			ushort dminor = getminor((*trp)->devid);
			if (cminor < dminor)
				break;
			if (cminor == dminor) {
				if (sflag == CLONEOPEN) {
					if (++cminor >= TCAP_NMINOR) {
						if (++cmajor >= TCAP_CMAJOR + TCAP_NMAJOR)
							break;
						cminor = 0;
					}
					continue;
				}
				ptrace(("ERROR: Requested device (%d,%d) in use\n", cmajor,
					cminor));
				return (EIO);
			}
		}
	}
	if (cmajor >= TCAP_CMAJOR + TCAP_NMINOR) {
		ptrace(("ERROR: No devices available\n"));
		return (ENXIO);
	}
	if (!(tcap = kmalloc(sizeof(*tcap), GFP_KERNEL))) {
		ptrace(("ERROR: Could not allocate private structure\n"));
		return (ENOMEM);
	}
	*devp = makedevice(cmajor, cminor);
	ptrace(("INFO: Making device major %d, minor %d\n", cmajor, cminor));
	bzero(tcap, sizeof(*tcap));
	q->q_ptr = WR(q)->q_ptr = tcap;
	tcap->q = q;
	tcap->devid = *devp;
	tcap->state = 0;
	switch (cminor) {
	case 1:		/* Layer Management stream */
		if (cmajor == TCAP_CMAJOR) {
			tcap_lmq = q;
			tcap->ops.r_prim = &tcap_m_r_prim;
			tcap->ops.w_prim = &tcap_m_w_prim;
			break;
		}
	case 2:		/* App Context stream */
		if (cmajor == TCAP_CMAJOR) {
			tcap_xlq = q;
			tcap->ops.r_prim = &tcap_x_r_prim;
			tcap->ops.w_prim = &tcap_x_w_prim;
			break;
		}
	default:		/* TCAP-User stream */
		tcap->ops.r_prim = &tcap_u_r_prim;
		tcap->ops.w_prim = &tcap_u_w_prim;
		break;
	}
	if ((tcap->next = *trp))
		tcap->next->prev = &tcap->next;
	tcap->prev = trp;
	*trp = tcap;
	return (0);
}
static int tcap_close(queue_t * q, int flag, cred_t * crp)
{
	tcap_t *tp = (tcap_t *) q->q_ptr;
	if (!tp)
		return (EIO);
	if (q == tcap_lmq)
		tcap_lmq = NULL;
	if (q == tcap_xlq)
		tcap_xlq = NULL;
	if (tp->sccp) {
		/* 
		 *  Detach this TCAP stream from whatever SCCP streams it is
		 *  using on the lower half.
		 */
		sccp_t *sccp = tp->sccp;
		if ((--tp->sccp->use_count) <= 0) {
			/* 
			 *  Unbind the SCCP stream if it is not being used by
			 *  any TCAP streams.
			 */
			mblk_t *mp;
			N_unbind_req_t *p;
			if (!(mp = allocb(sizeof(*p), BPRI_MED)))
				return (ENOSR);
			mp->b_datap->db_type = M_PCPROTO;
			p = (N_unbind_req_t *) mp->b_wptr;
			p->PRIM_type = N_UNBIND_REQ;
			mp->b_wptr += sizeof(*p);
			sccp->state = NS_WACK_UREQ;
			putnext(WR(sccp->q), mp);
			sccp->use_count = 0;
		}
		if ((*(tp->sccp_prev) = tp->sccp_next))
			tp->sccp_next->sccp_prev = tp->sccp_prev;
	}
	/* 
	 *  TODO:  Might want to do some more things to deallocate some TCAP
	 *  structures associated with the module.  I.e. some TCAP bindings.
	 */
	if (tp->rbid)
		unbufcall(xchg(&tp->rbid, 0));
	if (tp->wbid)
		unbufcall(xchg(&tp->wbid, 0));
	if ((*(tp->prev) = tp->next))
		tp->next->prev = tp->prev;
	WR(q)->q_ptr = RD(q)->q_ptr = NULL;
	kfree(tp);
	return (0);
}

/*
 *  =========================================================================
 *
 *  LIS STREAMS INITIALIZATION
 *
 *  =========================================================================
 */
static int tcap_initialized = 0;

#ifndef LIS_REGISTERED
static inline int tcap_init(void)
#else
__initfunc(int tcap_init(void))
#endif
{
	int err;
	if (tcap_initialized)
		return (0);
	tcap_initialized = 1;
	cmn_err(CE_NOTE, TCAP_BANNER);	/* console splash */
#ifndef LIS_REGISTERED
	if ((err = lis_register_strmod(&tcap_info, tcap_minfo.mi_idname)) < 0)
		cmn_err(CE_WARN, "tcap: couldn't register module!\n");
	return (err);
#endif
}

#ifndef LIS_REGISTERED
static inline void tcap_terminate(void)
#else
__initfunc(void tcap_terminate(void))
#endif
{
	if (!tcap_initialized)
		return;
	tcap_initialized = 0;
#ifndef LIS_REGISTERED
	if (lis_unregister_strmod(&tcap_info))
		cmn_err(CE_WARN, "tcap: couldn't unregister module!\n");
#endif
	return;
}

/*
 *  =========================================================================
 *
 *  LINUX MODULE INITIALIZATION
 *
 *  =========================================================================
 */

#ifdef MODULE
int init_module(void)
{
	return tcap_init();
}
void cleanup_module(void)
{
	tcap_terminate();
}
#endif


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

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

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