|
OpenSS7 SS7 for the Common Man |
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |
||||||||||||||||||||||||||
| Home | Overview | Status | News | Documentation | Resources | About | |||||||||||||||||||||
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);
}
|
|||||||||||||||||||||||||||
|
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
|
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |
|||||||||||||||||||||||||||