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/sdli/sdlm.c#ident "@(#) $RCSfile: sdlm.c,v $ $Name: $($Revision: 0.8.2.2 $) $Date: 2003/04/14 12:13:14 $" static char const ident[] = "$RCSfile: sdlm.c,v $ $Name: $($Revision: 0.8.2.2 $) $Date: 2003/04/14 12:13:14 $"; /* * A Signalling Data Link Multiplexor for the OpenSS7 SS7 Stack. * * This mux links SDL streams on the bottom of the multiplexor and presents * SDL streams on the top of the multiplexor. This permits independence * between the streams on the top side of the multiplexor and streams on the * bottom side of the multiplexor. A binding operation is performed to bind * an upper stream to a lower stream. */ #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 "../lock.h" #include "../debug.h" #include "../bufq.h" #include <ss7/lmi.h> #include <ss7/lmi_ioctl.h> #include <ss7/devi.h> #include <ss7/devi_ioctl.h> #include <ss7/sdli.h> #include <ss7/sdli_ioctl.h> #define DL_DESCRIP "SS7/SDL: (Signalling Data Link) MULTIPLEXING STREAMS DRIVER." #define DL_COPYRIGHT "Copyright (c) 1997-2002 OpenSS7 Corp. All Rights Reserved." #define DL_DEVICE "Supportes OpenSS7 SDL Drivers." #define DL_CONTACT "Brian Bidulock <bidulock@openss7.org>" #define DL_LICENSE "GPL" #define DL_BANNER DL_DESCRIP "\n" \ DL_COPYRIGHT "\n" \ DL_DEVICES "\n" \ DL_CONTACT "\n" MODULE_AUTHOR(DL_CONTACT); MODULE_DESCRIPTION(DL_DESCRIP); MODULE_SUPPORTED_DEVICE(DL_DEVICE); #ifdef MODULE_LICENSE MODULE_LICENSE(DL_LICENSE); #endif #if defined(LINUX_2_4)||defined(SOLARIS) #define INT int #else #define INT void #endif /* * ========================================================================= * * STREAMS Definitions * * ========================================================================= */ STATIC struct module_info dl_minfo = { 0, /* Module ID number */ "dl-mux", /* Module name */ 0, /* Min packet size accepted */ INFPSZ, /* Max packet size accepted */ 1 << 15, /* Hi water mark */ 1 << 10 /* Lo water mark */ }; STATIC int dl_open(queue_t *, dev_t *, int, int, cred_t *); STATIC int dl_close(queue_t *, int, cred_t *); STATIC INT dl_m_rput(queue_t *, mblk_t *); STATIC INT dl_m_rsrv(queue_t *); STATIC struct qinit dl_m_rinit = { dl_m_rput, /* Read put (msg from below) */ dl_m_rsrv, /* Read queue service */ dl_open, /* Each open */ dl_close, /* Last close */ NULL, /* Admin (not used) */ &dl_minfo, /* Information */ NULL /* Statistics */ }; STATIC INT dl_m_wput(queue_t *, mblk_t *); STATIC INT dl_m_wsrv(queue_t *); STATIC struct qinit dl_u_winit = { dl_m_wput, /* Write put (msg from above) */ dl_m_wsrv, /* Write queue service */ NULL, /* Each open */ NULL, /* Last close */ NULL, /* Admin (not used) */ &dl_minfo, /* Information */ NULL /* Statistics */ }; STATIC struct streamtab dl_m_info = { &dl_m_rinit, /* Upper read queue */ &dl_m_winit, /* Upper write queue */ NULL, /* Lower read queue */ NULL /* Lower write queue */ }; STATIC INT dl_u_rput(queue_t *, mblk_t *); STATIC struct qinit dl_u_rinit = { dl_u_rput, /* Read put (msg from below) */ NULL, /* Read queue service */ dl_open, /* Each open */ dl_close, /* Last close */ NULL, /* Admin (not used) */ &dl_minfo, /* Information */ NULL /* Statistics */ }; STATIC INT dl_u_wput(queue_t *, mblk_t *); STATIC struct qinit dl_u_winit = { dl_u_wput, /* Write put (msg from above) */ NULL, /* Write queue service */ NULL, /* Each open */ NULL, /* Last close */ NULL, /* Admin (not used) */ &dl_minfo, /* Information */ NULL /* Statistics */ }; STATIC INT dl_l_rput(queue_t *, mblk_t *); STATIC struct qinit dl_l_rinit = { dl_l_rput, /* Read put (msg from below) */ NULL, /* Read queue service */ NULL, /* Each open */ NULL, /* Last close */ NULL, /* Admin (not used) */ &dl_minfo, /* Information */ NULL /* Statistics */ }; STATIC INT dl_l_wput(queue_t *, mblk_t *); STATIC struct qinit dl_l_winit = { dl_l_wput, /* Write put (msg from above) */ NULL, /* Write queue service */ NULL, /* Each open */ NULL, /* Last close */ NULL, /* Admin (not used) */ &dl_minfo, /* Information */ NULL /* Statistics */ }; STATIC struct streamtab dl_u_info = { &dl_u_rinit, /* Upper read queue */ &dl_u_winit, /* Upper write queue */ &dl_l_rinit, /* Lower read queue */ &dl_l_winit /* Lower write queue */ }; /* * ========================================================================= * * Private Structure and Caches * * ========================================================================= */ typedef struct dl { struct dl *next; struct dl **prev; union { dev_t devid; int muxid; } u; queue_t *rq; queue_t *wq; ulong ppa; } dl_t; #define DL_PRIV(__q) ((dl_t *)((__q)->q_ptr)) /* * ------------------------------------------------------------------------- * * Caches * * ------------------------------------------------------------------------- */ STATIC kmem_cache_t *dl_cachep = NULL; STATIC int dl_cachep_allocated = 0; STATIC void dl_term_caches(void) { if (dl_cachep) { if (dl_cachep_allocated) kmem_cache_destroy(dl_cachep); dl_cachep = NULL; } return; } STATIC int dl_init_caches(void) { if (!dl_cachep) { if (!(dl_cachep = kmem_cache_create("dl_cachep", sizeof(dl_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) { dl_term_caches(); return (ENOMEM); } dl_cachep_allocated = 1; } return (0); } STATIC dl_t *dl_alloc_priv(queue_t * q) { dl_t *dl; if ((dl = kmem_cache_alloc(dl_cacep, SLAB_ATOMIC))) { MOD_INC_USE_COUNT; bzero(dl, sizeof(*dl)); dl->rq = RD(q); dl->wq = WR(q); DL_PRIV(dl->rq) = DL_PRIV(dl->wq) = dl; } return (dl); } STATIC void dl_free_priv(queue_t * q) { dl_t *dl = DL_PRIV(q); DL_PRIV(dl->rq) = DL_PRIV(dl->wq) = NULL; dl->rq = NULL; dl->wq = NULL; kmem_cache_free(dl_cachep, dl); MOD_DEC_USE_COUNT; } /* * ========================================================================= * * Primitives * * ========================================================================= */ STATIC INLINE int lmi_ok_ack_reply(queue_t * q, mblk_t * mp, int prim, int state) { lmi_ok_ack_t *p; mp->b_wptr = mp->b_rptr; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_OK_ACK; p->lmi_correct_primitive = prim; p->lmi_state = state; qreply(q, mp); return (0); } STATIC INLINE int lmi_error_ack_reply(queue_t * q, mblk_t * mp, int prim, int state, int err) { lmi_error_ack_t *p; /* return a negative acknowledgement */ mp->b_wptr = mp->b_rptr; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_ERROR_ACK; p->lmi_error_primitive = prim; p->lmi_state = state; p->lmi_errno = err > 0 ? 0 : -err; p->lmi_reason = err > 0 ? err : LMI_SYSERR; qreply(q, mp); return (err); } STATIC INLINE int m_error_reply(queue_t * q, mblk_t * mp, int err) { mp->b_wptr = mp->b_rptr; mp->b_datap->db_type = M_ERROR; *(mp->b_wptr)++ = err < 0 ? -err : err; *(mp->b_wptr)++ = err < 0 ? -err : err; qreply(q, mp); return (err); } /* * LMI_ATTACH_REQ: * ----------------------------------- */ STATIC INLINE int lmi_attach_req(queue_t * q, mblk_t * mp) { int err; size_t mlen = mp->b_wptr - mp->b_rptr; lmi_attach_req_t *m = ((typeof(m)) mp->b_rptr); if (mlen < sizeof(*m) || mlen < 2 * sizeof(ulong)) goto lprotoshort; if (q->q_next) goto loutstate; { dl_t *du = DL_PRIV(q), *dl; ulong ppa = *((ulong *) & m->lmi_ppa); for (dl = dl_links; dl && dl->ppa != ppa; dl = dl->next); if (!dl || dl->rq->q_next) goto lbadppa; /* link queues together */ du->wq->q_next = dl->wq; dl->rq->q_next = du->rq; return lmi_ok_ack_reply(q, mp, LMI_ATTACH_REQ, LMI_DISABLED); } lbadppa: err = LMI_BADPPA; ptrace(("ERROR: PPA in use\n")); goto error; loutstate: err = LMI_OUTSTATE; ptrace(("ERROR: would place i/f out of state\n")); goto error; lprotoshort: err = LMI_PROTOSHORT; ptrace(("ERROR: proto block to short\n")); goto error; error: return lmi_error_ack_reply(q, mp, LMI_ATTACH_REQ, LMI_UNATTACHED, err); } /* * LMI_DETACH_REQ: * ----------------------------------- */ STATIC INLINE int lmi_detach_req(queue_t * q, mblk_t * mp) { int err; size_t mlen = mp->b_wptr - mp->b_rptr; lmi_detach_req_t *m = ((typeof(m)) mp->b_rptr); if (mlen < sizeof(*m)) goto lprotoshort; if (!q->q_next) goto loutstate; { dl_t *du = DL_PRIV(q); dl_t *dl = DL_PRIT(q->q_next); /* disconnect them */ du->wq->q_next = NULL; dl->rq->q_next = NULL; { lmi_ok_ack_t *p; mp->b_wptr = mp->b_rptr; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_OK_ACK; p->lmi_correct_primitive = LMI_DETACH_REQ; p->lmi_state = LMI_UNATTACHED; qreply(q, mp); return (0); } } loutstate: err = LMI_OUTSTATE; ptrace(("ERROR: would place i/f out of state\n")); goto error; lprotoshort: err = LMI_PROTOSHORT; ptrace(("ERROR: proto block too short\n")); goto error; error: return lmi_error_ack_reply(q, mp, LMI_DETACH_REQ, LMI_UNATTACHED, err); } /* * ========================================================================= * * STREAMS Message Handling * * ========================================================================= */ /* * CTRL Queues * ----------------------------------- */ STATIC int dl_r_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: case M_PROTO: case M_PCPROTO: case M_FLUSH: default: } } STATIC int dl_w_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: case M_PROTO: case M_PCPROTO: case M_FLUSH: case M_IOCTL: default: } } STATIC int dl_putq(queue_t * q, mblk_t * mp, int (*proc) (queue_t *, mblk_t *)) { if (mp->b_datap->db_type >= QPCTL && !q->q_count) { int rtn; switch ((rtn = (*proc) (q, mp))) { case QR_DONE: freemsg(mp); case QR_ABSORBED: break; case QR_TRIMMED: freeb(mp); break; case QR_LOOP: if (!q->q_next) { qreply(q, mp); break; } 7 B case QR_PASSALONG:if (q->q_next) { putnext(q, mp); break; } rtn = -ENONOTSUPP; default: ptrace(("ERROR: (q dropping) %d\n", rtn)); freemsg(mp); break; case QR_DISABLE: putq(q, mp); rtn = 0; break; case QR_PASSFLOW: if (mp->b_datap->db_type >= QPCTL || canputnext(q)) { putnext(q, mp); break; } case -ENOBUFS: case -EBUYS: case -EAGAIN: case -ENOMEM: putq(q, mp); break; } return (rtn); } else { putq(q, mp); return (0); } } STATIC int dl_srvq(queue_t * q, int (*proc) (queue_t *, mblk_t *)) { int rtn; mblk_t *mp; while ((mp = getq(q))) { switch ((rtn = (*proc) (q, mp))) { case QR_DONE: freemsg(mp); case QR_ABSORBED: continue; case QR_TRIMMED: freeb(mp); continue; case QR_LOOP: if (!q->q_next) { qreply(q, mp); continue; } case QR_PASSALONG: if (q->q_next) { putnext(q, mp); continue; } rtn = -ENONOTSUPP; default: ptrace(("ERROR: (q dropping) %d\n", rtn)); freemsg(mp); continue; case QR_DISABLE: ptrace(("ERROR: (q disabling) %d\n", rtn)); noenable(q); putq(q, mp); rtn = 0; break; case QR_PASSFLOW: if (mp->b_datap->db_type >= QPCTL || canputnext(q)) { putnext(q, mp); continue; } case -ENOBUFS: case -EBUYS: case -EAGAIN: case -ENOMEM: ptrace(("ERROR: (q stalled) %d\n", rtn)); 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: (q dropping) %d\n", rtn)); freemsg(mp); continue; } } return (0); } STATIC INT dl_m_rput(queue_t * q, mblk_t * mp) { return ((INT) dl_putq(q, mp, &dl_r_prim)); } STATIC INT dl_m_rsrv(queue_t * q) { return ((INT) dl_srvq(q, &dl_r_prim)); } STATIC INT dl_m_wput(queue_t * q, mblk_t * mp) { return ((INT) dl_putq(q, mp, &dl_w_prim)); } STATIC INT dl_m_wsrv(queue_t * q) { return ((INT) dl_srvq(q, &dl_w_prim)); } /* * UPPER MUX Queues * ----------------------------------- */ STATIC INT dl_u_rput(queue_t * q, mblk_t * mp) { putnext(q, mp); return ((INT) (0)); } STATIC INT dl_u_wput(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_PROTO: case M_PCPROTO: { long prim = *((long *) mp->b_rptr); switch (prim) { case LMI_ATTACH_REQ: return ((INT) lmi_attach_req(q, mp)); case LMI_DETACH_REQ: return ((INT) lmi_detach_req(q, mp)); case LMI_INFO_REQ: case LMI_ENABLE_REQ: case LMI_DISABLE_REQ: case LMI_OPTMGMT_REQ: if (q->q_next) { putnext(q, mp); return ((INT) (0)); } ptrace(("ERROR: would place i/f out of state\n")); return lmi_error_ack_reply(q, mp, prim, LMI_UNATTACHED, LMI_OUTSTATE); case SDL_DAEDT_TRANSMISSION_REQ: case SDL_DAEDT_START_REQ: case SDL_DAEDR_START_REQ: if (q->q_next) { putnext(q, mp); return ((INT) (0)); } return ((INT) m_error_reply(q, mp, EPIPE)); default: freemsg(mp); return ((INT) m_error_reply(q, mp, EPROTO)); } } break; case M_FLUSH: if (q->q_next) putnext(q, mp); else qreply(q, mp); break; default: if (q->q_next) putnext(q, mp); else { freemsg(mp); return ((INT) (-EOPNOTSUPP)); } break; } return ((INT) (0)); } /* * LOWER MUX Queues * ----------------------------------- */ STATIC INT dl_l_rput(queue_t * q, mblk_t * mp) { if (q->q_next) { putnext(q, mp); return ((INT) (0)); } if (mp->b_datap->db_type == M_FLUSH) { qreply(q, mp); return ((INT) (0)); } ptrace(("ERROR: receive message for detached stream\n")); freemsg(mp); return ((INT) (-EOPNOTSUPP)); } STATIC INT dl_l_wput(queue_t * q, mblk_t * mp) { putnext(q, mp); return ((INT) (0)); } /* * ========================================================================= * * OPEN and CLOSE * * ========================================================================= */ dl_t *dl_opens = NULL; dl_t *dl_links = NULL; dl_t *dl_ctrls = NULL; /* * OPEN * ------------------------------------------------------------------------- */ STATIC int dl_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp) { int cmajor = getmajor(*devp); int cminor = getminor(*devp); int lmajor = 0; int lminor = 0; dl_t *dl = NULL, **dlp = &dl; if (sflags == MODOPEN || WR(q)->q_next) { ptrace(("ERROR: Cannot open as module\n")); return (EIO); } if (LM_CMAJOR <= cmajor && cmajor < LM_CMAJOR + LM_NMAJOR && crp->cr_uid != 0) { ptrace(("ERROR: r00t permission needed for control stream\n")); return (EPERM); } if (DL_PRIV(q)) { ptrace(("INFO: Already open\n")); return (0); } if ((cmajor == DL_CMAJOR || cmajor == LM_CMAJOR) && cminor == 0) { ptrace(("INFO: Clone open in effect\n")); sflag == CLONEOPEN; cminor = 1; } if (DL_CMAJOR <= cmajor && cmajor < DL_CMAJOR + DL_NMAJOR) { dlp = &dl_opens; lminor = DL_NMINOR; lmajor = DL_CMAJOR + DL_NMAJOR; } if (LM_CMAJOR <= cmajor && cmajor < LM_CMAJOR + LM_NMAJOR) { dlp = &dl_ctrls; lminor = LM_NMINOR; lmajor = LM_CMAJOR + LM_NMAJOR; } for (dlp = &dl_opens; *dlp; dlp = &((*dlp)->next)) { ushort dmajor = getmajor((*dlp)->u.devid); if (cmajor < dmajor) break; if (cmajor == dmajor) { ushort dminor = getminor((*dlp)->u.devid); if (cminor < dminor) break; if (cminor == dminor) { if (sflag != CLONEOPEN) { ptrace(("ERROR: Requested device in use\n")); return (EIO); } if (++cminor > lminor) { if (++cmajor >= lmajor) break; cminor = 0; } continue; } } } if (cmajor >= lmajor) { ptrace(("ERROR: No device available\n")); return (ENXIO); } if (!(dl = dl_alloc_priv(q))) { ptrace(("ERROR: Could not allocate private structure\n")); return (ENOMEM); } *devp = makedevice(cmajor, cminor); dl->u.devid = *devp; if ((dl->next = *sdpl)) dl->next->prev = &dl->next; dl->prev = dlp; *dlp = dl; return (0); } /* * CLOSE * ------------------------------------------------------------------------- */ STATIC int dl_close(queue_t * q, int flag, cred_t * crp) { dl_t *dl = DL_PRIV(q); if ((*(dl->prev) = dl->next)) dl->next->prev = dl->prev; dl->prev = NULL; dl->next = NULL; dl_free_priv(q); return (0); } /* * ========================================================================= * * LiS Module Initialization * * ========================================================================= */ STATIC int dl_initialized = 0; STATIC int dl_init(void) { if (!dl_initialized) { int i; int err; for (i = 0; i < DL_NMAJOR; i++) { if ((err = lis_register_strdev(DL_CMAJOR + i, &dl_u_info, DL_NMINOR, dl_minfo.mi_idname)) < 0) { cmn_err(CE_WARN, "dl-mux: couldn't register driver cmajor %d\n", DL_CMAJOR); } } for (i = 0; i < LM_NMAJOR; i++) { if ((err = lis_register_strdev(LM_CMAJOR + i, &dl_m_info, LM_NMINOR, dl_minfo.mi_idname)) < 0) { cmn_err(CE_WARN, "dl-mux: couldn't register driver cmajor %d\n", LM_CMAJOR); } } dl_init_caches(); dl_initialized = 1; } return (0); } STATIC void dl_terminate(void) { if (dl_initialized) { int i; for (i = 0; i < DL_NMAJOR; i++) { if ((err = lis_unregister_strdev(DL_CMAJOR + i)) < 0) { cmn_err(CE_WARN, "dl-mux: couldn't unregister driver cmajor %d\n", DL_CMAJOR); } } for (i = 0; i < LM_NMAJOR; i++) { if ((err = lis_unregister_strdev(LM_CMAJOR + i)) < 0) { cmn_err(CE_WARN, "dl-mux: couldn't unregister driver cmajor %d\n", LM_CMAJOR); } } dl_term_caches(); dl_initialized = 0; } return; } /* * ======================================================================= * * Kernel Module Initialization * * ======================================================================= */ int init_module(void) { cmn_err(CE_NOTE, DL_BANNER); return dl_init(); } void cleanup_module(void) { return dl_terminate(); }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |