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_upper.c


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



#ident "@(#) $RCSfile: tcap_upper.c,v $ $Name:  $($Revision: 0.8.2.2 $) $Date: 2003/04/03 19:51:40 $"

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

#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 "../debug.h"
#include "../bufq.h"

#include "tcap.h"
#include "tcap_data.h"
#include "tcap_msgs.h"
#include "tcap_ctrl.h"
#include "tcap_prov.h"
#include "../sccpi/sccp_user.h"

/*
 *  =========================================================================
 *
 *  TCAP-User --> TCAP (Downstream Primitives received from upstream)
 *
 *  =========================================================================
 */
static inline int tc_error_reply(queue_t * q, mblk_t * pdu, int prim, int err)
{
	mblk_t *mp;
	if ((mp = tc_error_ack(prim, err))) {
		freemsg(pdu);
		qreply(q, mp);
		return (0);
	}
	return (-ENOBUFS);
}

/*
 *  TCAP Options parsing.
 */
static int parse_options(queue_t * q, mblk_t * pdu, int prim, caddr_t opt_ptr, size_t opt_len)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	(void) err;
	(void) tcap;
	if (opt_len && opt_len < sizeof(uint32_t))
		return tc_error_reply(q, pdu, prim, TCBADOPT);
	/* 
	 *  Decode the options....
	 */
	return (0);
}

/*
 *  TR_INFO_REQ           0 - Information request                     
 *  ---------------------------------------------------------------
 */
static int tr_info_req(queue_t * q, mblk_t * pdu)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	TR_info_req_t *p = (TR_info_req_t *) pdu->b_rptr;
	(void) err;
	(void) tcap;
	(void) p;
	if (pdu->b_wptr - pdu->b_rptr < sizeof(*p))
		return tc_error_reply(q, pdu, TR_INFO_REQ, -EMSGSIZE);
	/* 
	 *  TODO: process primitive...
	 */
	freemsg(pdu);
	return (0);
}

/*
 *  TR_BIND_REQ           1 - Bind to network address                 
 *  ---------------------------------------------------------------
 */
static int tr_bind_req(queue_t * q, mblk_t * pdu)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	TR_bind_req_t *p = (TR_bind_req_t *) pdu->b_rptr;
	(void) err;
	(void) tcap;
	(void) p;
	if (pdu->b_wptr - pdu->b_rptr < sizeof(*p))
		return tc_error_reply(q, pdu, TR_BIND_REQ, -EMSGSIZE);
	/* 
	 *  TODO: process primitive...
	 */
	tcap->state = NS_IDLE;
	freemsg(pdu);
	return (0);
}

/*
 *  TR_UNBIND_REQ         2 - Unbind from network address             
 *  ---------------------------------------------------------------
 */
static int tr_unbind_req(queue_t * q, mblk_t * pdu)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	TR_unbind_req_t *p = (TR_unbind_req_t *) pdu->b_rptr;
	(void) err;
	(void) tcap;
	(void) p;
	if (pdu->b_wptr - pdu->b_rptr < sizeof(*p))
		return tc_error_reply(q, pdu, TR_UNBIND_REQ, -EMSGSIZE);
	/* 
	 *  TODO: process primitive...
	 */
	tcap->state = NS_IDLE;
	freemsg(pdu);
	return (0);
}

/*
 *  TR_OPTMGMT_REQ	  5 - Options management request
 *  ---------------------------------------------------------------
 */
static int tr_optmgmt_req(queue_t * q, mblk_t * pdu)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	TR_optmgmt_req_t *p = (TR_optmgmt_req_t *) pdu->b_rptr;
	(void) err;
	(void) tcap;
	(void) p;
	if (pdu->b_wptr - pdu->b_rptr < sizeof(*p))
		return tc_error_reply(q, pdu, TR_OPTMGMT_REQ, -EMSGSIZE);
	/* 
	 *  TODO: process primitive...
	 */
	tcap->state = NS_IDLE;
	freemsg(pdu);
	return (0);
}

/*
 *  TR_UNI_REQ         7 - Unidirectional request                  
 *  ---------------------------------------------------------------
 */
static int tr_uni_req(queue_t * q, mblk_t * pdu)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	TR_uni_req_t *p = (TR_uni_req_t *) pdu->b_rptr;
	(void) err;
	(void) tcap;
	(void) p;
	if (pdu->b_wptr - pdu->b_rptr < sizeof(*p))
		return tc_error_reply(q, pdu, TR_UNI_REQ, -EMSGSIZE);
	/* 
	 *  TODO: process primitive...
	 */
	tcap->state = NS_IDLE;
	freemsg(pdu);
	return (0);
}

/*
 *  TR_BEGIN_REQ          7 - Begin transaction request               
 *  ---------------------------------------------------------------
 */
static int tr_begin_req(queue_t * q, mblk_t * pdu)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	TR_begin_req_t *p = (TR_begin_req_t *) pdu->b_rptr;
	(void) err;
	(void) tcap;
	(void) p;
	if (pdu->b_wptr - pdu->b_rptr < sizeof(*p))
		return tc_error_reply(q, pdu, TR_BEGIN_REQ, -EMSGSIZE);
	/* 
	 *  TODO: process primitive...
	 */
	tcap->state = NS_IDLE;
	freemsg(pdu);
	return (0);
}

/*
 *  TR_BEGIN_RES          8 - Begin transaction response               
 *  ---------------------------------------------------------------
 */
static int tr_begin_res(queue_t * q, mblk_t * pdu)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	TR_begin_res_t *p = (TR_begin_res_t *) pdu->b_rptr;
	(void) err;
	(void) tcap;
	(void) p;
	if (pdu->b_wptr - pdu->b_rptr < sizeof(*p))
		return tc_error_reply(q, pdu, TR_BEGIN_RES, -EMSGSIZE);
	/* 
	 *  TODO: process primitive...
	 */
	tcap->state = NS_IDLE;
	freemsg(pdu);
	return (0);
}

/*
 *  TR_CONT_REQ	          9 - Continue transaction request            
 *  ---------------------------------------------------------------
 */
static int tr_cont_req(queue_t * q, mblk_t * pdu)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	TR_cont_req_t *p = (TR_cont_req_t *) pdu->b_rptr;
	(void) err;
	(void) tcap;
	(void) p;
	if (pdu->b_wptr - pdu->b_rptr < sizeof(*p))
		return tc_error_reply(q, pdu, TR_CONT_REQ, -EMSGSIZE);
	/* 
	 *  TODO: process primitive...
	 */
	tcap->state = NS_IDLE;
	freemsg(pdu);
	return (0);
}

/*
 *  TR_END_REQ           10 - End transaction request                 
 *  ---------------------------------------------------------------
 */
static int tr_end_req(queue_t * q, mblk_t * pdu)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	TR_end_req_t *p = (TR_end_req_t *) pdu->b_rptr;
	(void) err;
	(void) tcap;
	(void) p;
	if (pdu->b_wptr - pdu->b_rptr < sizeof(*p))
		return tc_error_reply(q, pdu, TR_END_REQ, -EMSGSIZE);
	/* 
	 *  TODO: process primitive...
	 */
	tcap->state = NS_IDLE;
	freemsg(pdu);
	return (0);
}

/*
 *  TR_ABORT_REQ         11 - Abort transaction request               
 *  ---------------------------------------------------------------
 */
static int tr_abort_req(queue_t * q, mblk_t * pdu)
{
	int err;
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	TR_abort_req_t *p = (TR_abort_req_t *) pdu->b_rptr;
	(void) err;
	(void) tcap;
	(void) p;
	if (pdu->b_wptr - pdu->b_rptr < sizeof(*p))
		return tc_error_reply(q, pdu, TR_ABORT_REQ, -EMSGSIZE);
	/* 
	 *  TODO: process primitive...
	 */
	tcap->state = NS_IDLE;
	freemsg(pdu);
	return (0);
}

int (*tr_dprim[]) (queue_t *, mblk_t *) = {
#define USER_DSTR_FIRST	TR_INFO_REQ
	tr_info_req,		/* TR_INFO_REQ 0 - Information request */
	    tr_bind_req,	/* TR_BIND_REQ 1 - Bind to network address */
	    tr_unbind_req,	/* TR_UNBIND_REQ 2 - Unbind from network address */
	    tr_optmgmt_req,	/* TR_OPTMGMT_REQ 5 - Options management request */
	    tr_uni_req,		/* TR_UNI_REQ 6 - Unidirectional request */
	    tr_begin_req,	/* TR_BEGIN_REQ 7 - Begin transaction request */
	    tr_begin_res,	/* TR_BEGIN_RES 8 - Begin transaction response-Continue request */
	    tr_cont_req,	/* TR_CONT_REQ 9 - Continue transaction request */
	    tr_end_req,		/* TR_END_REQ 10 - End transaction request */
	    tr_abort_req	/* TR_ABORT_REQ 11 - Abort transaction request */
#define USER_DSTR_LAST  TR_ABORT_REQ
};

static int tcap_w_data(queue_t * q, mblk_t * pdu)
{
	tcap_t *tcap = (tcap_t *) q->q_ptr;
	if (tcap->state != TRS_DATA_XFER) {
		/* 
		 *  If we are not in a connection oriented state, we return an
		 *  M_ERROR on all subsequent write operations on the stream
		 *  will fail.
		 */
		mblk_t *mp;
		if (!(mp = m_error(0, EPROTO)))
			return (-ENOBUFS);
		qreply(q, mp);
		freemsg(pdu);
		return (0);
	}
	/* 
	 *  This is an TCAP data message from above.  This is only valid in
	 *  connection-oriented states.
	 */
	/* 
	 *  TODO: pass data message to SCCP.
	 */
	return (0);
}
static int tcap_w_proto(queue_t * q, mblk_t * mp)
{
	uint32_t prim = *((uint32_t *) mp->b_rptr);
	if (USER_DSTR_FIRST <= prim && prim <= USER_DSTR_LAST && tr_dprim[prim])
		return (*tr_dprim[prim]) (q, mp);
	/* 
	 *  FIXME: We should probably send an M_ERROR here.
	 */
	return (-EOPNOTSUPP);
}

/*
 *  =========================================================================
 *
 *  M_CTL Processing
 *
 *  =========================================================================
 *  We have no defined module to module controls for either TCAP or SCCP.
 */

static int tcap_w_ctl(queue_t * q, mblk_t * pdu)
{
	/* 
	 *  FIXME: Remove this TCAP-User from the SCCP-Provider...
	 */
	return m_error_reply(q, pdu, EFAULT, EFAULT);
}

/*
 *  =========================================================================
 *
 *  M_IOCTL Processing
 *
 *  =========================================================================
 *
 *  I_LINK, I_PLINK Handling
 *  -------------------------------------------------------------------------
 *  SCCP Provider streams can be linked under the TCAP multiplexing driver.  We
 *  permit normal TCAP streams to perform an I_LINK of SCCP Provider streams
 *  under the SCCP driver, but we only permit the TCAP control stream to
 *  perform an I_PLINK of SCCP Provider streams.  This is so that if the
 *  configuration daemon crashes it can come back an the configuration has not
 *  been destroyed.
 *
 *  When linked, we generate an N_INFO_REQ downstream to the newly linked SCCP
 *  Provider.  The purpose of this is to discover the response from the
 *  provider as to which state the provider is currently in and which
 *  addresses the SCCP Provider might be currently bound to.  This permits the
 *  caller to bind the SCCP Provider.
 */
static inline int tcap_i_link(queue_t * q, mblk_t * pdu, struct linkblk *lp)
{
	mblk_t *mp;
	sccp_t *sccp;
	queue_t *lq;
//      ensure( lp, return(-EFAULT) );
	lq = RD(lp->l_qbot);
	if (!(mp = sccp_info_req()))
		return (-ENOBUFS);
	if (!(sccp = kmalloc(sizeof(*sccp), GFP_KERNEL))) {
		freemsg(mp);
		return (-ENOMEM);
	}
	bzero(sccp, sizeof(*sccp));
	sccp->q = RD(lq);
	sccp->lmq = RD(q);
	sccp->muxid = lp->l_index;
	sccp->state = NS_UNBND;
	RD(lq)->q_ptr = WR(lq)->q_ptr = sccp;
	putnext(WR(lq), mp);	/* get info on SCCP provider */
	if (sccp->state != NS_IDLE) {
		/* sccp providers must be prebound */
		RD(lq)->q_ptr = WR(lq)->q_ptr = NULL;
		kfree(sccp);
		return (-EINVAL);
	}
	if ((sccp->next = tcap_links))
		sccp->next->prev = &sccp->next;
	sccp->prev = &tcap_links;
	return (lp->l_index);
}

/*
 *  I_UNLINK, I_PUNLINK Handling
 *  -------------------------------------------------------------------------
 *  When we unlink, if we still have referencing upper TCAP-User streams, we
 *  send each of the 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
 *  TCAP-User is a stream head.
 */
static inline int tcap_i_unlink(queue_t * q, struct linkblk *lp)
{
	int err;
	sccp_t *sccp;
	uint muxid;
//      ensure( lp, return(-EFAULT) );
	muxid = lp->l_index;
	for (sccp = tcap_links; sccp && sccp->muxid != muxid; sccp = sccp->next);
	if (!sccp)
		return (-EINVAL);
	if ((err = m_flush_all(q, NULL, FLUSHW, 0)))
		return (err);
	if ((err = m_hangup_all(q, NULL)))
		return (err);
	if (sccp->state == NS_IDLE) {
		mblk_t *mp;
		if (!(mp = sccp_unbind_req()))
			return (-ENOBUFS);
		sccp->state = NS_WACK_UREQ;
		putnext(WR(sccp->q), mp);
	}
	if ((*(sccp->prev) = sccp->next))
		sccp->next->prev = sccp->prev;
	sccp->q->q_ptr = WR(sccp->q)->q_ptr = NULL;
	kfree(sccp);
	return (0);
}
static int tcap_w_ioctl(queue_t * q, mblk_t * mp)
{
	int ret = -EINVAL;
	void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL;
	struct iocblk *iocp = (struct iocblk *) mp->b_wptr;
	int cmd = iocp->ioc_cmd;
	switch (_IOC_TYPE(cmd)) {
	case __SID:
		switch (cmd) {
		case I_PLINK:
			if (RD(q) != tcap_lmq) {
				ret = -EPERM;
				break;
			}
		case I_LINK:
			ret = tcap_i_link(q, mp, arg);
			break;
		case I_PUNLINK:
			if (RD(q) != tcap_lmq) {
				ret = -EPERM;
				break;
			}
		case I_UNLINK:
			ret = tcap_i_unlink(q, arg);
			break;
		}
		break;
	case TCAP_IOC_MAGIC:
#if 0
		if (iocp->ioc_count >= _IOC_SIZE(cmd)) {
			int nr = _IOC_NR(cmd);
			ret = -EOPNOTSUPP;
			if (0 <= nr && nr < sizeof(tcap_ioctl) / sizeof(int (*)(void))
			    && tcap_ioctl[nr])
				ret = (*tcap_ioctl[nr]) (q, cmd, arg);
		}
#endif
		break;
	default:
		ret = -EOPNOTSUPP;
		break;
	}
	mp->b_datap->db_type = ret < 0 ? M_IOCNAK : M_IOCACK;
	iocp->ioc_error = ret < 0 ? -ret : 0;
	iocp->ioc_rval = ret < 0 ? -1 : ret;
	qreply(q, mp);
	return (0);
}

/*
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 */
/*
 *  WRITE PUT and SERVICE (Message from above TCAP-User --> TCAP)
 *  -------------------------------------------------------------------------
 */
int tcap_u_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return tcap_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return tcap_w_proto(q, mp);
	case M_CTL:
		return tcap_w_ctl(q, mp);
	case M_FLUSH:
		return tcap_w_flush(q, mp);
	case M_IOCTL:
		return tcap_w_ioctl(q, mp);
	}
	return (-EOPNOTSUPP);
}


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

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

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