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