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/ua/ua.c#ident "@(#) $RCSfile: ua.c,v $ $Name: $($Revision: 0.8.2.3 $) $Date: 2003/04/03 19:51:46 $" static char const ident[] = "$RCSfile: ua.c,v $ $Name: $($Revision: 0.8.2.3 $) $Date: 2003/04/03 19:51:46 $"; #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 <ss7/ua_lm.h> #include <ss7/ua_lm_ioctl.h> #include "../lock.h" #include "../debug.h" #include "../bufq.h" #include "ua.h" #include "ua_data.h" struct ua_driver *ua_driver = NULL; struct ua_driver *m2ua_driver = NULL; struct ua_driver *m3ua_driver = NULL; struct ua_driver *isua_driver = NULL; struct ua_driver *sua_driver = NULL; struct ua_driver *tua_driver = NULL; /* * ========================================================================= * * Allocate, Deallocated and Cache Private structures. * * ========================================================================= */ /* * Uppers * ----------------------------------- */ static int ua_free_upper(ua_driver_t * drv, dp_t * dp) { int err; if (dp->ops && dp->ops->remove) if ((err = (*dp->ops->remove) (dp))) return (err); if ((*(dp->prev) = dp->next)) dp->next->prev = dp->prev; dp->prev = NULL; dp->next = NULL; if (dp->bid) unbufcall(dp->bid); if (dp == drv->lm) drv->lm = NULL; dp->rq->q_ptr = NULL; dp->wq->q_ptr = NULL; kmem_cache_free(drv->cachep, dp); return (0); } static int ua_alloc_upper(ua_driver_t * drv, queue_t * q, dp_t ** dpp, dev_t * devp, uint type) { int err; dp_t *dp; if (!(dp = kmem_cache_alloc(drv->cachep, SLAB_ATOMIC))) { bzero(dp, drv->psize); if ((dp->next = *dpp)) dp->next->prev = &dp->next; dp->prev = dpp; *dpp = dp; dp->drv = drv; dp->id.dev = *devp; dp->type = type; dp->rq = RD(q); dp->wq = WR(q); dp->mq = RD(q); dp->ops = dp == drv->lm ? drv->lmq_ops : drv->ss7_ops; dp->rq->q_ptr = dp->wq->q_ptr = dp; if ((err = (*dp->ops->create) (dp))) { ua_free_upper(drv, dp); return (err); } return (0); } return (ENOMEM); } static dp_t *ua_find_upper(ua_driver_t * drv, dev_t dev) { dp_t *dp; for (dp = drv->opens_list; dp && dp->id.dev != dev; dp = dp->next); return (dp); } /* * Lowers * ----------------------------------- */ static int ua_free_lower(ua_driver_t * drv, lp_t * lp) { int err; if (lp->ops && lp->ops->remove) if ((err = (*lp->ops->remove) (lp))) return (err); if ((*lp->prev) = lp->next) lp->prev->next = lp->prev; lp->prev = NULL; lp->next = NULL; if (lp->bid) unbufcall(lp->bid); lp->rq->q_ptr = NULL; lp->wq->q_ptr = NULL; kmem_cache_free(drv->cachep, lp); return (0); } static lp_t *lp; ua_alloc_lower(ua_driver_t * drv, queue_t * q, dp_t * dp, linkblk * lb) { lp_t *lp; if (!(lp = kmem_cache_alloc(drv->cachep, SLAB_ATOMIC))) { bzero(lp, drv->psize); if ((lp->next = drv->links_list)) lp->next->prev = &lp->next; lp->prev = &drv->links_list; drv->links_list = lp; lp->drv = drv; lp->id.mux = lb->l_index; lp->rq = RD(lb->l_qbot); lp->wq = WR(lb->l_qbot); lp->mq = RD(q); lp->ops = NULL; lp->rq->q_ptr = lp->wq->q_ptr = lp; } return (lp); } static lp_t *ua_find_lower(ua_driver_t * drv, int mux) { lp_t *lp; for (lp = drv->links_list; lp && lp->id.mux != mux; lp = lp->next); return (lp); } /* * ========================================================================= * * M_IOCTL Processing * * ========================================================================= */ /* * I_LINK, I_PLINK Handling * ------------------------------------------------------------------------- * SS7 or SGP Provider or ASP User streams can be linked under the UA * multiplexing driver. We permit normal SS7 streams to perform an I_LINK of * SS7 or SGP provider or ASP user streams under the UA driver, but we only * permit the SS7 control stream to perform an I_PLINK of SS7 or SGP provider * or ASP user streams. This is so that if the configuration daemon crashes * it can come back and the configuration has not been destroyed. * * When linked, we generate the appropriate information request downstream to * the newly linked SS7 or SGP provider or ASP user stream. The purpose of * this is to discover the response from the stream as to which state the * stream is currently in and which addresses might be currently bound. This * permits the caller to bind the SS7 or SGP provider or ASP user streams. */ static inline int ua_i_link(queue_t * q, mblk_t * pdu, struct linkblk *lb) { lp_t *lp; dp_t *dp = (dp_t *) q->q_ptr; queue_t *lq; ensure(lb, return (-EFAULT)); lq = RD(lb->l_qbot); if (!(lp = kmalloc(sizeof(*lp), GFP_KERNEL))) return (-ENOMEM); bzero(lp, sizeof(*lp)); lp->rq = RD(lq); lp->wq = WR(lq); lp->mq = RD(q); lp->id.mux = lb->l_index; lp->state = 0; RD(lq)->q_ptr = WR(lq)->q_ptr = lp; if ((lp->next = dp->links)) lp->next->prev = &lp->next; lp->prev = &dp->links; noenable(lq); /* disable queue till configured */ return (lb->l_index); } /* * I_UNLINK, I_PUNLINK Handling * ------------------------------------------------------------------------- * When we unlink, if we still have referencing upper SS7-user streams, we * send each of them 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 SS7-user is a stream head. */ extern int m_flush_all(queue_t * q, mblk_t * mp, uint flag, uint band); extern int m_hangup_all(queue_t * q, mblk_t * mp); static inline int ua_i_unlink(queue_t * q, struct linkblk *lb) { int err; lp_t *lp; dp_t *dp = (dp_t *) q->q_ptr; uint mux; ensure(lb, return (-EFAULT)); mux = lb->l_index; for (lp = dp->links; lp && lp->id.mux != mux; lp = lp->next); if (!lp) return (-EINVAL); if ((err = m_flush_all(q, NULL, FLUSHW, 0))) return (err); if ((err = m_hangup_all(q, NULL))) return (err); if ((*(lp->prev) = lp->next)) lp->next->prev = lp->prev; lp->rq->q_ptr = lp->wq->q_ptr = NULL; kfree(lp); return (0); } static int ua_w_ioctl(queue_t * q, mblk_t * pdu) { int ret = -EINVAL; void *arg = pdu->b_cont ? pdu->b_cont->b_rptr : NULL; struct iocblk *iocp = (struct iocblk *) pdu->b_wptr; int cmd = iocp->ioc_cmd; switch (_IOC_TYPE(cmd)) { case __SID: switch (cmd) { case I_PLINK: if (q->q_ptr != ua_driver->lm) { ret = -EPERM; break; } case I_LINK: ret = ua_i_link(q, pdu, arg); break; case I_PUNLINK: if (q->q_ptr != ua_driver->lm) { ret = -EPERM; break; } case I_UNLINK: ret = ua_i_unlink(q, arg); break; } break; case UA_IOC_MAGIC: default: ret = -EOPNOTSUPP; break; } pdu->b_datap->db_type = ret < 0 ? M_IOCNAK : M_IOCACK; iocp->ioc_error = ret < 0 ? -ret : 0; iocp->ioc_rval = ret > 0 ? ret : -1; qreply(q, pdu); return (0); } /* * ------------------------------------------------------------------------- * * I_LINK, I_PLINK, I_UNLINK, I_PUNLINK Handling * * ------------------------------------------------------------------------- */ /* * I_LINK * ---------------------------------------- * SS7 or SGP Provider or ASP User streams can be linked under the UA * multiplexing driver. We only permit normal SS7 streams to perform an * I_LINK of SS7 or SGP provider or ASP user streams under the UA driver, and * we only permit the SS7 control stream to perform an I_PLINK of SS7 or SGP * provider or ASP user streams. This is so that if the configuration daemon * crashes it can come back and the configuration has not been destroyed. */ static int ua_i_link(queue_t * q, mblk_t * mp) { lp_t *lp; dp_t *dp = (dp_t *) q->q_ptr; queue_t *lq; struct linkblk *lb; ensure(q, return (-EFAULT)); ensure(mp, return (-EFAULT)); ensure(mp->b_cont, return (-EFAULT)); lb = (struct linkblk *) mp->b_cont->b_rptr; if (!(lp = ua_alloc_lower(drv, q, dp, lb))) return (-ENOMEM); } /* * I_PLINK * ---------------------------------------- */ static int ua_i_plink(queue_t * q, mblk_t * mp) { ua_t *m; ua_t *m = (ua_t *) q->q_ptr; struct linkblk *lb; ensure(q, return (-EFAULT)); ensure(mp, return (-EFAULT)); ensure(mp->b_cont, return (-EFAULT)); lb = (struct linkblk *) mp->b_cont->b_rptr; /* only r00t ctrlq is allowed to make permanent links */ if (!(m = ua_alloc_lower(lb))) return (-ENOMEM); } /* * I_UNLINK * ---------------------------------------- */ static int ua_i_unlink(queue_t * q, mblk_t * mp) { ua_t *m; ua_t *m = (ua_t *) q->q_ptr; struct linkblk *lb; ensure(q, return (-EFAULT)); ensure(mp, return (-EFAULT)); ensure(mp->b_cont, return (-EFAULT)); lb = (struct linkblk *) mp->b_cont->b_rptr; if ((m = ua_find_lower(lb->l_index))) { if (m->mq == RD(lb->l_qtop)) { { fixme(("Send out of service if upper exists.\n")); m->rq->q_next = NULL; WR(m->mq) = NULL; m->mq = NULL; m->lq = NULL; m->uq = NULL; ua_free_lower(m); return (0); } } rare(); return (-EPERM); } rare(); return (-ENXIO); } /* * I_PUNLINK * ---------------------------------------- */ static int ua_i_punlink(queue_t * q, mblk_t * mp) { ua_t *m; struct linkblk *lb; ensure(q, return (-EFAULT)); ensure(mp, return (-EFAULT)); ensure(mp->b_cont, return (-EFAULT)); /* only r00t ctrlq is allowed to undo permanent links */ lb = (struct linkblk *) mp->b_cont->b_rptr; if ((m = ua_find_lower(lb->l_index))) { { fixme(("Send out of service if upper exists.\n")); ua_t *m = m->uq ? (ua_t *) m->uq->q_ptr : NULL; m->uq = NULL; m->lq = NULL; ua_free_lower(m); return (0); } } rare(); return (-ENXIO); } /* * ========================================================================= * * STREAMS Message Handling * * ========================================================================= */ /* * ------------------------------------------------------------------------- * * M_IOCDATA Handling * * ------------------------------------------------------------------------- */ static int ua_w_iocdata(queue_t * q, mblk_t * mp) { ua_t *ua = (ua_t *) q->q_ptr; if (!ua->ops || !ua->ops->w_prim) return (4); return ((*ua->ops->w_prim) (q, mp)); } static int ua_m_iocdata(queue_t * q, mblk_t * mp) { ua_t *ua = (ua_t *) q->q_ptr; if (!ua->ops || !ua->ops->r_prim) return (4); return ((*ua->ops->r_prim) (q, mp)); } /* * ------------------------------------------------------------------------- * * M_IOCTL Handling * * ------------------------------------------------------------------------- */ static int ua_reply_ioc(queue_t * q, mblk_t * mp, int err) { struct iocblk *iocp = (struct iocblk *) mp->b_rptr; if (err < 0) { mp->b_datap->db_type = M_IOCNAK; iocp->ioc_error = -err; iocp->ioc_rval = -1; } else { mp->b_datap->db_type = M_IOCACK; iocp->ioc_error = 0; iocp->ioc_rval = 0; } qreply(q, mp); return (1); } static int ua_m_ioctl(queue_t * q, mblk_t * mp) { int err = -EOPNOTSUPP; ua_t *ua = (ua_t *) q->q_ptr; struct iocblk *iocp = (struct iocblk *) mp->b_rptr; switch (_IOC_TYPE(iocp->ioc_cmd)) { case __SID: switch (iocp->ioc_cmd) { case I_PLINK: err = ua_i_plink(q, mp); break; case I_PUNLINK: err = ua_i_punlink(q, mp); break; case I_LINK: case I_UNLINK: err = -EPERM; break; } break; default: if (!ua->ops || !ua->ops->w_prim) return (4); err = (*ua->ops->r_prim) (q, mp); break; } return ua_reply_ioc(q, mp, err); } static int ua_p_ioctl(queue_t * q, mblk_t * mp) { int err = -EOPNOTSUPP; ua_t *ua = (ua_t *) q->q_ptr; struct iocblk *iocp = (struct iocblk *) mp->b_rptr; struct linkblk *lb = (struct linkblk *) mp->b_cont->b_rptr; switch (_IOC_TYPE(iocp->ioc_cmd)) { case __SID: switch (iocp->ioc_cmd) { case I_LINK: err = ua_i_link(q, mp); break; case I_UNLINK: err = ua_i_unlink(q, mp); break; case I_PLINK: case I_PUNLINK: err = -EPERM; break; } break; default: if (!ua->ops || !ua->ops->w_prim) return (4); err = (*ua->ops->r_prim) (q, mp); break; } return ua_reply_ioc(q, mp, err); } static int ua_w_ioctl(queue_t * q, mblk_t * mp) { ua_t *ua = (ua_t *) q->q_ptr; if (ua->type & UA_STRM_LM) return ua_m_ioctl(q, mp); else return ua_p_ioctl(q, mp); } /* * ------------------------------------------------------------------------- * * M_FLUSH Handling * * ------------------------------------------------------------------------- */ static int ua_m_flush(queue_t * q, mblk_t * mp, const uint8_t mflag, const uint8_t oflag); { if (mp->b_rptr[0] & mflag) { 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] &= ~FLUSHW; } if (mp->b_rptr[0] & oflag && !(mp->b_flag & MSGNOLOOP)) { queue_t *oq = q->q_other; if (mp->b_rptr[0] & FLUSHBAND) flushband(oq, mp->b_rptr[1], FLUSHALL); else flushq(oq, FLUSHALL); mp->b_flag |= MSGNOLOOP; qreply(q, mp); return (1); } return (0); } static int ua_w_flush(queue_t * q, mblk_t * mp) { return ua_m_flush(q, mp, FLUSHW, FLUSHR); } static int ua_r_flush(queue_t * q, mblk_t * mp) { return ua_m_flush(q, mp, FLUSHR, FLUSHW); } /* * ------------------------------------------------------------------------- * * Other handling * * ------------------------------------------------------------------------- */ static int ua_w_other(queue_t * q, mblk_t * mp) { ua_t *ua = (ua_t *) q->q_ptr; if (!ua->ops || !ua->ops->w_prim) return (4); return ((*ua->ops->w_prim) (q, mp)); } static int ua_r_other(queue_t * q, mblk_t * mp) { ua_t *ua = (ua_t *) q->q_ptr; if (!ua->ops || !ua->ops->r_prim) return (4); return ((*ua->ops->r_prim) (q, mp)); } /* * ========================================================================= * * STREAMS PUTQ and SRVQ routines * * ========================================================================= * * UA WPUT * ------------------------------------------------------------------------- */ static INT ua_wput(queue_t q, mblk_t * mp) { int rtn; ensure(q, return ((INT) (-EFAULT))); ensure(mp, return ((INT) (-EFAULT))); if (mp->b_datap->db_type < QPCTL && q->q_count) { seldom(); putq(q, mp); return (INT) (0); } switch (mp->b_datap->db_type) { case M_IOCTL: rtn = ua_w_ioctl(q, mp); break; case M_IOCDATA: rtn = ua_w_iocdata(q, mp); break; case M_FLUSH: rtn = ua_w_flush(q, mp); break; default: rtn = ua_w_other(q, mp); break; } switch (rtn) { 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; /* fall through */ default: ptrace(("Dropping error = %d\n", rtn)); freemsg(mp); return (INT) (rtn); 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); return (INT) (0); } return (INT) (0); } /* * UA WSRV * ------------------------------------------------------------------------- */ static INT ua_wsrv(queue_t * q) { int rtn; mblk_t *mp; while ((mp = getq(q))) { switch (mp->b_datap->db_type) { case M_IOCTL: rtn = ua_w_ioctl(q, mp); break; case M_IOCDATA: rtn = ua_w_iocdata(q, mp); break; case M_FLUSH: rtn = ua_w_flush(q, mp); break; default: rtn = ua_w_other(q, mp); break; } switch (rtn) { 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; } rtn = -EOPNOTSUPP; default: ptrace(("Dropping error = %d\n", rtn)); freemsg(mp); continue; 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 */ if (mp->b_datap->db_type < QPCTL) { putbq(q, mp); return (INT) (0); } ptrace(("Dropping error = %d\n", rtn)); freemsg(mp); continue; } } return (INT) (0); } /* * UA RPUT * ------------------------------------------------------------------------- */ static INT ua_rput(queue_t q, mblk_t * mp) { int rtn; ensure(q, return ((INT) (-EFAULT))); ensure(mp, return ((INT) (-EFAULT))); if (mp->b_datap->db_type < QPCTL && q->q_count) { seldom(); putq(q, mp); return (INT) (0); } switch (mp->b_datap->db_type) { case M_FLUSH: rtn = ua_r_flush(q, mp); break; default: rtn = ua_r_other(q, mp); break; } switch (rtn) { 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; /* fall through */ default: ptrace(("Dropping error = %d\n", rtn)); freemsg(mp); return (INT) (rtn); 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); return (INT) (0); } return (INT) (0); } /* * UA RSRV * ------------------------------------------------------------------------- */ static INT ua_rsrv(queue_t * q) { int rtn; mblk_t *mp; while ((mp = getq(q))) { switch (mp->b_datap->db_type) { case M_FLUSH: rtn = ua_r_flush(q, mp); break; default: rtn = ua_r_other(q, mp); break; } switch (rtn) { 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; } rtn = -EOPNOTSUPP; default: ptrace(("Dropping error = %d\n", rtn)); freemsg(mp); continue; 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 */ if (mp->b_datap->db_type < QPCTL) { putbq(q, mp); return (INT) (0); } ptrace(("Dropping error = %d\n", rtn)); freemsg(mp); continue; } } return (INT) (0); } /* * BUFSRV calls service routine * ------------------------------------------------------------------------- */ static void ua_bufsrv(long data) { ua_t *ua = (ua_t *) data; if (ua->bid) { ua->bid = 0; qenable(ua->rq); qenable(ua->wq); } } /* * ========================================================================= * * OPEN and CLOSE * * ========================================================================= * * OPEN * ------------------------------ */ int ua_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp) { int err; int type = 0; int cmajor = getmajor(*devp); int cminor = getminor(*devp); dp_t **dpp; ua_driver_t *drv; switch (cmajor) { case UA_CMAJOR: drv = ua_driver; type |= UA_PROT_NONE; break; case M2UA_CMAJOR: drv = m2ua_driver; type |= UA_PROT_SL; break; case M3UA_CMAJOR: drv = m3ua_driver; type |= UA_PROT_MTP; break; case ISUA_CMAJOR: drv = isua_driver; type |= -UA_PROT_ISUP; break; case SUA_CMAJOR: drv = sua_driver; type |= UA_PROT_SCCP; break; case TUA_CMAJOR: drv = tua_driver; type |= UA_PROT_TCAP; break; default: drv = NULL; break; } /* * FIXME: * Later we might want to consider demand loading our own * modules. (LiS-2.13 does not support demand loading of * drivers.) What we would do is link a small demand loader * with LiS STREAMS and have it register a bunch of majors * and then have it call. */ if (!drv || cmajor != drv->cmajor) return (EIO); if (q->q_ptr != NULL) return (0); /* already open */ if (sflag == MODOPEN || WR(q)->q_next) return (EIO); /* cannot push as module */ if (cminor == drv->nminor) { if (drv->lm) return (ENXIO); /* only one */ if (crp->cr_uid != 0) return (EPERM); /* need r00t */ dpp = &drv->lm; type |= UA_STRM_LM; } else { if (!cminor) sflag = CLONEOPEN; if (sflag == CLONEOPEN) cminor = 1; dpp = &drv->opens_list; for (; *dpp && getmajor((*dpp)->id.dev) < cmajor; dpp = &(*dpp)->next); for (; *dpp && cminor < drv->nminor; dpp = &(*dpp)->next) { int dminor = getminor((*dpp)->id.dev); if (cminor < dminor) break; if (cminor == dminor) { if (sflag == CLONEOPEN) { cminor++; continue; } rare(); return (EIO); } } if (cminor > drv->nminor) return (ENXIO); type |= UA_STRM_SS7; } *devp = makedevice(cmajor, cminor); if ((err = ua_alloc_upper(drv, q, dpp, devp, type))) return (err); return (0); } /* * CLOSE * ------------------------------ */ int ua_close(queue_t * q, int flag, cred_t * crp) { dp_t *dp = (dp_t *) q->q_ptr; if (!dp) return (EIO); return ua_free_upper(dp->drv, dp); }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |