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/slsi/sls.c


File /code/strss7/drivers/slsi/sls.c



#ident "@(#) $RCSfile: sls.c,v $ $Name:  $($Revision: 0.8.2.4 $) $Date: 2003/04/14 12:13:27 $"

static char const ident[] =
    "$RCSfile: sls.c,v $ $Name:  $($Revision: 0.8.2.4 $) $Date: 2003/04/14 12:13:27 $";

/*
 *  This is an SLS (Signalling Link Set) loadable module which provides all of
 *  the capabilities of the SLSI.  It can be used by drivers in two fashions:
 *
 *  1)  Drivers can provide a set of missing functions by registering with the
 *      module using the ls_register_driver.  This relieves much of the
 *      STREAMS burden from the driver implementation and permits the driver
 *      to be more portable across the Linux, STREAMS, and BSD implementation
 *      of the OpenSS7 stack.
 *
 *  2)  Driver which implement the OpenSS7 SLI can be linked under this
 *      multiplexor to form an SLSI driver implementation.
 *
 *  Both fashions permit SS7 Level 2 and 3 compliant state machines to be
 *  tested once, but used by many drivers.
 */

#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>
#include <ss7/sdti.h>
#include <ss7/sdti_ioctl.h>
#include <ss7/sli.h>
#include <ss7/sli_ioctl.h>
#include <ss7/slsi.h>
#include <ss7/slsi_ioctl.h>

#include "../lmi/lm.h"
#include "../devi/dev.h"
#include "../sdli/sdl.h"
#include "../sdti/sdt.h"
#include "../sli/sl.h"
#include "../slsi/sls.h"
#include "sls_codec.h"		/* message parsing and building */

#define LS_DESCRIP	"SS7/SLS: (Signalling Link Set) STREAMS DRIVER."
#define LS_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  All Rights Reserved."
#define LS_DEVICES	"Supports OpenSS7 SLS drivers."
#define LS_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define LS_LICENSE	"GPL"
#define LS_BANNER	LS_DESCRIP     "\n" \
			LS_COPYRIGHT   "\n" \
			LS_DEVICES     "\n" \
			LS_CONTACT     "\n"

#ifdef MODULE
MODULE_AUTHOR(LS_CONTACT);
MODULE_DESCRIPTION(LS_DESCRIP);
MODULE_SUPPORTED_DEVICE(LS_DEVICES);
#ifdef MODULE_LICENSE
MODULE_LICENSE(LS_LICENSE);
#endif
#define MODULE_STATIC static
#else
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#define MODULE_STATIC
#endif

#ifdef LS_DEBUG
static int ls_debug = LS_DEBUG;
#else
static int ls_debug = 2;
#endif

#define DEBUG_LEVEL ls_debug

#ifndef LS_CMAJOR
#define LS_CMAJOR 249
#endif
#define LS_NMINOR 255

/*
 *  =======================================================================
 *
 *  STREAMS Definitions
 *
 *  =======================================================================
 */

static struct module_info ls_minfo = {
	0,				/* Module ID number */
	"ls",				/* Module name */
	5,				/* Min packet size accepted */
	272,				/* Max packet size accepted */
	8 * 272,			/* Hi water mark */
	1 * 272				/* Lo water mark */
};

static void ls_rput(queue_t *, mblk_t *);
static void ls_rsrv(queue_t *);
static int ls_open(queue_t *, dev_t *, int, int, cred_t *);
static int ls_close(queue_t *, int, cred_t *);

static struct qinit ls_rinit = {
	ls_rput,			/* Read put (msg from below) */
	ls_rsrv,			/* Read queue service */
	ls_open,			/* Each open */
	ls_close,			/* Last close */
	NULL,				/* Admin (not used) */
	&ls_minfo,			/* Information */
	NULL				/* Statistics */
};

static void ls_wput(queue_t *, mblk_t *);
static void ls_wsrv(queue_t *);

static struct qinit ls_winit = {
	ls_wput,			/* Write put (msg from above) */
	ls_wsrv,			/* Write queue service */
	NULL,				/* Each open */
	NULL,				/* Last close */
	NULL,				/* Admin (not used) */
	&ls_minfo,			/* Information */
	NULL				/* Statistics */
};

static struct module_info lk_minfo = {
	0,				/* Module ID number */
	"ls-lower",			/* Module name */
	5,				/* Min packet size accepted */
	272,				/* Max packet size accepted */
	8 * 272,			/* Hi water mark */
	1 * 272				/* Lo water mark */
};

static void lk_rput(queue_t *, mblk_t *);
static void lk_rsrv(queue_t *);

static struct qinit lk_rinit = {
	lk_rput,			/* Write put (msg from above) */
	lk_rsrv,			/* Write queue service */
	NULL,				/* Each open */
	NULL,				/* Last close */
	NULL,				/* Admin (not used) */
	&lk_minfo,			/* Information */
	NULL				/* Statistics */
};

static void lk_wput(queue_t *, mblk_t *);
static void lk_wsrv(queue_t *);

static struct qinit lk_winit = {
	lk_wput,			/* Write put (msg from above) */
	lk_wsrv,			/* Write queue service */
	NULL,				/* Each open */
	NULL,				/* Last close */
	NULL,				/* Admin (not used) */
	&lk_minfo,			/* Information */
	NULL				/* Statistics */
};

#ifdef MODULE
static
#endif
struct streamtab ls_info = {
	&ls_rinit,			/* Upper read queue */
	&ls_winit,			/* Upper write queue */
	&lk_rinit,			/* Lower read queue */
	&lk_winit			/* Lower write queue */
};

/*
 *  =======================================================================
 *
 *  LMI PROTOCOL CONFIGURATION IOCTLs
 *
 *  =======================================================================
 */

static int ls_iocgoptions(ls_t * ls, int cmd, void *arg)
{
	DTRACE;
	bcopy(&ls->option, arg, _IOC_SIZE(cmd));
	return (0);
}
static int ls_iocsoptions(ls_t * ls, int cmd, void *arg)
{
	DTRACE;
	bcopy(arg, &ls->option, _IOC_SIZE(cmd));
	return (0);
}
static int ls_iocgconfig(ls_t * ls, int cmd, void *arg)
{
	DTRACE;
	bcopy(&ls->config, arg, _IOC_SIZE(cmd));
	return (0);
}
static int ls_iocsconfig(ls_t * ls, int cmd, void *arg)
{
	signed long int *src, *dst, *end;
	DTRACE;
	dst = (signed long int *) &ls->config;
	end = (signed long int *) ((&ls->config) + 1);
	src = arg;
	while (dst < end) {
		if (*src != -1)
			*dst = *src;
		dst++;
		src++;
	}
	return (0);
}
static int ls_iocgstatem(ls_t * ls, int cmd, void *arg)
{
	DTRACE;
	bcopy(&ls->statem, arg, _IOC_SIZE(cmd));
	return (0);
}
static int ls_iocsstatsp(ls_t * ls, int cmd, void *arg)
{
	DTRACE;
	(void) arg;
	return EOPNOTSUPP;
}
static int ls_iocgstatsp(ls_t * ls, int cmd, void *arg)
{
	DTRACE;
	(void) arg;
	return EOPNOTSUPP;
}
static int ls_ioccstats(ls_t * ls, int cmd, void *arg)
{
	DTRACE;
	(void) arg;
	bzero(&ls->stats, _IOC_SIZE(cmd));
	return (0);
}
static int ls_iocgstats(ls_t * ls, int cmd, void *arg)
{
	DTRACE;
	bcopy(&ls->stats, arg, _IOC_SIZE(cmd));
	return (0);
}
static int ls_iocsnotify(ls_t * ls, int cmd, void *arg)
{
	caddr_t dst, src, end;
	DTRACE;
	src = arg;
	dst = (caddr_t) & ls->notify;
	end = (caddr_t) ((&ls->notify) + 1);
	while (dst < end)
		*dst++ |= *src++;
	return (0);
}
static int ls_ioccnotify(ls_t * ls, int cmd, void *arg)
{
	caddr_t dst, src, end;
	DTRACE;
	src = arg;
	dst = (caddr_t) & ls->notify;
	end = (caddr_t) ((&ls->notify) + 1);
	while (dst < end)
		*dst++ &= ~*src++;
	return (0);
}
static int ls_iocgnotify(ls_t * ls, int cmd, void *arg)
{
	DTRACE;
	bcopy(&ls->notify, arg, _IOC_SIZE(cmd));
	return (0);
}

static int (*ls_ioc_lmiops[]) (ls_t *, int, void *) = {
	ls_iocgoptions,		/* LS_IOCGOPTIONS */
	    ls_iocsoptions,	/* LS_IOCSOPTIONS */
	    ls_iocgconfig,	/* LS_IOCGCONFIG */
	    ls_iocsconfig,	/* LS_IOCSCONFIG */
	    NULL,		/* LS_IOCTCONFIG */
	    NULL,		/* LS_IOCCCONFIG */
	    ls_iocgstatem,	/* LS_IOCGSTATEM */
	    NULL,		/* LS_IOCCMRESET */
	    ls_iocgstatsp,	/* LS_IOCGSTATSP */
	    ls_iocsstatsp,	/* LS_IOCSSTATSP */
	    ls_iocgstats,	/* LS_IOCGSTATS */
	    ls_ioccstats,	/* LS_IOCCSTATS */
	    ls_iocgnotify,	/* LS_IOCGNOTIFY */
	    ls_iocsnotify,	/* LS_IOCSNOTIFY */
	    ls_ioccnotify	/* LS_IOCCNOTIFY */
};

/*
 *  =======================================================================
 *
 *  LS->RT Service Primitives (M_CTL, M_PROTO, M_PCPROTO
 *
 *  =======================================================================
 */

static inline void ls_uprim_msg(ls_t * ls, int type, int prim, mblk_t * mp)
{
	mp->b_datap->db_type = type;
	*((ls_long *) mp->b_rptr) = prim;
	ls_rput(ls->rq, mp);
}
static inline void ls_uprim_link(ls_t * ls, int type, int prim, int slc)
{
	mblk_t *mp;
	if ((mp = allocb(sizeof(ls_link_t), BPRI_HI))) {
		ls_link_t *p = (ls_link_t *) mp->b_wptr;
		mp->b_wptr += sizeof(ls_link_t);
		mp->b_datap->db_type = type;
		p->primitive = prim;
		p->lpc = ls->config.lpc;
		p->apc = ls->config.apc;
		p->slc = slc;
		ls_rput(ls->rq, mp);
	}
}
static inline void ls_uprim(ls_t * ls, int type, int prim)
{
	mblk_t *mp;
	if ((mp = allocb(sizeof(ls_long), BPRI_HI))) {
		mp->b_datap->db_type = type;
		*((ls_long *) mp->b_wptr)++ = prim;
		ls_rput(ls->rq, mp);
	}
}
static inline void ls_uprim_cong(ls_t * ls, int type, int prim)
{
	mblk_t *mp;
	if ((mp = allocb(sizeof(ls_congestion_t), BPRI_HI))) {
		ls_congestion_t *p = (ls_congestion_t *) mp->b_rptr;
		mp->b_wptr += sizeof(ls_congestion_t);
		mp->b_datap->db_type = type;
		p->primitive = prim;
		p->cong_status = ls->statem.cong_status;
		p->disc_status = ls->statem.disc_status;
		ls_rput(ls->rq, mp);
	}
}

static void ls_rt_message_for_routing(ls_t * ls, mblk_t * mp)
{
	ls_uprim_msg(ls, M_PROTO, LS_MESSAGE_FOR_ROUTING_IND, mp);
}
static void ls_rt_message_for_discrimination(ls_t * ls, mblk_t * mp)
{
	ls_uprim_msg(ls, M_PROTO, LS_MESSAGE_FOR_DISCRIMINATION_IND, mp);
}
static void ls_rt_link_inhibited(ls_t * ls, int slc)
{
	ls_uprim_link(ls, M_PROTO, LS_LINK_INHIBITED_IND, slc);
}
static void ls_rt_link_inhibit_denied(ls_t * ls, int slc)
{
	ls_uprim_link(ls, M_PROTO, LS_LINK_INHIBIT_DENIED_IND, slc);
}
static void ls_rt_link_inhibit_failed(ls_t * ls, int slc)
{
	ls_uprim_link(ls, M_PROTO, LS_LINK_INHIBIT_FAILED_IND, slc);
}
static void ls_rt_link_uninhibited(ls_t * ls, int slc)
{
	ls_uprim_link(ls, M_PROTO, LS_LINK_UNINHIBITED_IND, slc);
}
static void ls_rt_link_uninhibit_failed(ls_t * ls, int slc)
{
	ls_uprim_link(ls, M_PROTO, LS_LINK_UNINHIBIT_FAILED_IND, slc);
}
static void ls_rt_force_uninhibit_failed(ls_t * ls)
{
	ls_uprim(ls, M_PROTO, LS_FORCE_UNINHIBIT_FAILED_IND);
}
static void ls_rt_available(ls_t * ls)
{
	ls_uprim(ls, M_PROTO, LS_AVAILABLE_IND);
}
static void ls_rt_unavailable(ls_t * ls)
{
	ls_uprim(ls, M_PROTO, LS_UNAVAILABLE_IND);
}
static void ls_rt_retrieved_message(ls_t * ls, mblk_t * mp)
{
	ls_uprim_msg(ls, M_PROTO, LS_RETRIEVED_MESSAGE_IND, mp);
}
static void ls_rt_retrieval_complete(ls_t * ls)
{
	ls_uprim(ls, M_PROTO, LS_RETRIEVAL_COMPLETE_IND);
}
static void ls_rt_congested(ls_t * ls, int cong, int disc)
{
	ls->statem.cong_status = cong;
	ls->statem.disc_status = disc;
	ls_uprim_cong(ls, M_PCPROTO, LS_CONGESTED_IND);
}
static void ls_rt_congestion_ceased(ls_t * ls, int cong, int disc)
{
	ls->statem.cong_status = cong;
	ls->statem.disc_status = disc;
	ls_uprim_cong(ls, M_PCPROTO, LS_CONGESTION_CEASED_IND);
}
static void ls_rt_link_in_service_at_level_2(ls_t * ls)
{
	ls_uprim(ls, M_PROTO, LS_LINK_IN_SERVICE_AT_LEVEL_2_IND);
}

static ls_ucalls_t ls_mux_ucalls = {
	ls_rt_message_for_routing,	/* ls_msg_for_routing */
	ls_rt_message_for_discrimination,	/* ls_msg_for_discrim */
	ls_rt_link_inhibited,		/* ls_inhibited */
	ls_rt_link_inhibit_denied,	/* ls_inhibit_denied */
	ls_rt_link_inhibit_failed,	/* ls_inhibit_failed */
	ls_rt_link_uninhibited,		/* ls_uninhibited */
	ls_rt_link_uninhibit_failed,	/* ls_uninhibit_failed */
	ls_rt_force_uninhibit_failed,	/* ls_force_unihibit_failed */
	ls_rt_available,		/* ls_available */
	ls_rt_unavailable,		/* ls_unavailable */
	ls_rt_retrieved_message,	/* ls_retrieved_message */
	ls_rt_retrieval_complete,	/* ls_retrieval_complete */
	ls_rt_congested,		/* ls_congested */
	ls_rt_congestion_ceased,	/* ls_congestion_ceased */
	ls_rt_link_in_service_at_level_2	/* ls_in_service_at_l2 */
};

/*
 *  =========================================================================
 *
 *  LK->SL Service Primitives (M_CTL, M_PROTO, M_PCPROTO)
 *
 *  =========================================================================
 */

static inline void lk_dprim_msg(lk_t * lk, int type, int prim, mblk_t * mp)
{
	if (mp->b_datap->db_type != M_DATA) {
		*((sl_long *) mp->b_rptr) = SL_PDU_REQ;
		mp->b_datap->db_type = type;
	}
	lk_wput(lk->wq, mp);
}
static inline void lk_dprim(lk_t * lk, int type, int prim)
{
	mblk_t *mp;
	if ((mp = allocb(sizeof(sl_long), BPRI_HI))) {
		mp->b_datap->db_type = type;
		*((sl_long *) mp->b_wptr)++ = prim;
		lk_wput(lk->wq, mp);
	}
}
static inline void lk_dprim_arg(lk_t * lk, int type, int prim, int arg)
{
	mblk_t *mp;
	if ((mp = allocb(2 * sizeof(sl_ulong), BPRI_HI))) {
		mp->b_datap->db_type = type;
		*((sl_long *) mp->b_wptr)++ = prim;
		*((sl_ulong *) mp->b_wptr)++ = arg;
		lk_wput(lk->wq, mp);
	}
}

static void lk_sl_pdu(lk_t * lk, mblk_t * mp)
{
	lk_dprim_msg(lk, M_PROTO, SL_PDU_REQ, mp);
}
static void lk_sl_emergency(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_EMERGENCY_REQ);
}
static void lk_sl_emergency_ceases(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_EMERGENCY_CEASES_REQ);
}
static void lk_sl_start(lk_t * lk)
{
	lk_dprim(lk, M_PROTO, SL_START_REQ);
}
static void lk_sl_stop(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_STOP_REQ);
}
static void lk_sl_retrieve_bsnt(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_RETRIEVE_BSNT_REQ);
}
static void lk_sl_retrieval_request_and_fsnc(lk_t * lk, int fsnc)
{
	lk_dprim_arg(lk, M_PROTO, SL_RETRIEVAL_REQUEST_AND_FSNC_REQ, fsnc);
}
static void lk_sl_resume(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_RESUME_REQ);
}
static void lk_sl_clear_buffers(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_CLEAR_BUFFERS_REQ);
}
static void lk_sl_clear_rtb(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_CLEAR_RTB_REQ);
}
static void lk_sl_local_processor_outage(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_LOCAL_PROCESSOR_OUTAGE_REQ);
}
static void lk_sl_congestion_discard(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_CONGESTION_DISCARD_REQ);
}
static void lk_sl_congestion_accept(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_CONGESTION_ACCEPT_REQ);
}
static void lk_sl_no_congestion(lk_t * lk)
{
	lk_dprim(lk, M_PCPROTO, SL_NO_CONGESTION_REQ);
}
static void lk_sl_power_on(lk_t * lk)
{
	lk_dprim(lk, M_PROTO, SL_POWER_ON_REQ);
}

/*
 *  =======================================================================
 *
 *  PROTOCOL STATE MACHINES
 *
 *  =======================================================================
 */

#include "sls_sm.h"		/* link set state machines */

/*
 *  =======================================================================
 *
 *  LS->LK Service Calls (M_CTL, M_PROTO, M_PCPROTO
 *
 *  =======================================================================
 */

static ls_mcalls_t ls_mux_dcalls = {
	ls_message_from_routing,	/* ls_msg_from_routing */
	ls_message_for_distribution,	/* ls_msg_for_dist */
	ls_message_for_rerouting,	/* ls_msg_for_reroute */
	ls_link_activate,		/* ls_lk_activate */
	ls_link_deactivate,		/* ls_lk_deactivate */
	ls_link_inhibit,		/* ls_lk_inhibit */
	ls_link_uninhibit,		/* ls_lk_uninhibit */
	ls_activate,			/* ls_activate */
	ls_deactivate,			/* ls_deactivate */
	ls_inhibit,			/* ls_inhibit */
	ls_uninhibit,			/* ls_uninhibit */
	ls_force_uninhibit,		/* ls_force_unihibit */
	ls_routing_outage,		/* ls_routing_outage */
	ls_routing_recovered,		/* ls_routing_recovered */
	ls_critical,			/* ls_critical */
	ls_noncritical,			/* ls_noncritical */
	ls_emergency,			/* ls_emerg */
	ls_emergency_ceases,		/* ls_emerg_ceases */
	ls_traffic_starts,		/* ls_traffic */
	ls_traffic_ends,		/* ls_no_traffic */
	ls_adjacent_sp_inaccessible,	/* ls_adj_inaccessible */
	ls_adjacent_sp_accessible	/* ls_adj_accessible */
};

/*
 *  =======================================================================
 *
 *  LS<-RT Service Primitives (M_CTL, M_PROTO, M_PCPROTO)
 *
 *  =======================================================================
 */

static void ls_message_for_routing_req(ls_t * ls, mblk_t * mp)
{
	ls->dcalls->ls_msg_from_routing(ls, mp);
}
static void ls_message_for_distribution_req(ls_t * ls, mblk_t * mp)
{
	ls->dcalls->ls_msg_for_dist(ls, mp);
}
static void ls_message_for_rerouting_req(ls_t * ls, mblk_t * mp)
{
	ls->dcalls->ls_msg_for_reroute(ls, mp);
}
static void ls_link_activate_req(ls_t * ls, mblk_t * mp)
{
	ls_link_t *p = (ls_link_t *) mp->b_rptr;
	int slc = p->slc;
	freemsg(mp);
	ls->dcalls->ls_lk_activate(ls, slc);
}
static void ls_link_deactivate_req(ls_t * ls, mblk_t * mp)
{
	ls_link_t *p = (ls_link_t *) mp->b_rptr;
	int slc = p->slc;
	freemsg(mp);
	ls->dcalls->ls_lk_deactivate(ls, slc);
}
static void ls_link_inhibit_req(ls_t * ls, mblk_t * mp)
{
	ls_link_t *p = (ls_link_t *) mp->b_rptr;
	int slc = p->slc;
	freemsg(mp);
	ls->dcalls->ls_lk_inhibit(ls, slc);
}
static void ls_link_uninhibit_req(ls_t * ls, mblk_t * mp)
{
	ls_link_t *p = (ls_link_t *) mp->b_rptr;
	int slc = p->slc;
	freemsg(mp);
	ls->dcalls->ls_lk_uninhibit(ls, slc);
}
static void ls_activate_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_activate(ls);
}
static void ls_deactivate_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_deactivate(ls);
}
static void ls_inhibit_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_inhibit(ls);
}
static void ls_uninhibit_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_uninhibit(ls);
}
static void ls_force_uninhibit_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_force_uninhibit(ls);
}
static void ls_routing_outage_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_routing_outage(ls);
}
static void ls_routing_recovered_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_routing_recovered(ls);
}
static void ls_critical_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_critical(ls);
}
static void ls_noncritical_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_noncritical(ls);
}
static void ls_emergency_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_emerg(ls);
}
static void ls_emergency_ceases_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_emerg_ceases(ls);
}
static void ls_traffic_starts_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_traffic(ls);
}
static void ls_traffic_ends_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_no_traffic(ls);
}
static void ls_adjacent_sp_inaccessible_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_adj_inaccessible(ls);
}
static void ls_adjacent_sp_accessible_req(ls_t * ls, mblk_t * mp)
{
	freemsg(mp);
	ls->dcalls->ls_adj_accessible(ls);
}

static void (*ls_rt_ops[]) (ls_t *, mblk_t *) = {
	ls_message_for_routing_req,	/* LS_MESSAGE_FOR_ROUTING_REQ */
	    ls_message_for_distribution_req,	/* LS_MESSAGE_FOR_DISTRIBUTION_REQ */
	    ls_message_for_rerouting_req,	/* LS_MESSAGE_FOR_REROUTING_REQ */
	    ls_link_activate_req,	/* LS_LINK_ACTIVATE_REQ */
	    ls_link_deactivate_req,	/* LS_LINK_DEACTIVATE_REQ */
	    ls_link_inhibit_req,	/* LS_LINK_INHIBIT_REQ */
	    ls_link_uninhibit_req,	/* LS_LINK_UNINHIBIT_REQ */
	    ls_activate_req,	/* LS_ACTIVATE_REQ */
	    ls_deactivate_req,	/* LS_DEACTIVATE_REQ */
	    ls_inhibit_req,	/* LS_INHIBIT_REQ */
	    ls_uninhibit_req,	/* LS_UNINHIBIT_REQ */
	    ls_force_uninhibit_req,	/* LS_FORCE_UNIHIBIT_REQ */
	    ls_routing_outage_req,	/* LS_ROUTING_OUTAGE_REQ */
	    ls_routing_recovered_req,	/* LS_ROUTING_RECOVERED_REQ */
	    ls_critical_req,	/* LS_CRITICAL_REQ */
	    ls_noncritical_req,	/* LS_NONCRITICAL_REQ */
	    ls_emergency_req,	/* LS_EMERGENCY_REQ */
	    ls_emergency_ceases_req,	/* LS_EMERGENCY_CEASES_REQ */
	    ls_traffic_starts_req,	/* LS_TRAFFIC_STARTS_REQ */
	    ls_traffic_ends_req,	/* LS_TRAFFIC_ENDS_REQ */
	    ls_adjacent_sp_inaccessible_req,	/* LS_ADJACENT_SP_INACCESSIBLE_REQ */
	    ls_adjacent_sp_accessible_req	/* LS_ADJACENT_SP_ACCESSIBLE_REQ */
};

/*
 *  =======================================================================
 *
 *  LK<-SL Service Primitives (M_CTL, M_PROTO, M_PCPROTO)
 *
 *  =======================================================================
 */

static void sl_pdu_ind(lk_t * lk, mblk_t * mp)
{
	sl_pdu(lk, mp);
}
static void sl_link_congested_ind(lk_t * lk, mblk_t * mp)
{
	sl_prim_t *p = (sl_prim_t *) mp->b_rptr;
	int cong = p->link_cong_ind.sl_cong_status;
	int disc = p->link_cong_ind.sl_disc_status;
	freemsg(mp);
	sl_link_congested(lk, cong, disc);
}
static void sl_link_congestion_ceased_ind(lk_t * lk, mblk_t * mp)
{
	sl_prim_t *p = (sl_prim_t *) mp->b_rptr;
	int cong = p->link_cong_ceased_ind.sl_cong_status;
	int disc = p->link_cong_ceased_ind.sl_disc_status;
	freemsg(mp);
	sl_link_congestion_ceased(lk, cong, disc);
}
static void sl_retrieved_message_ind(lk_t * lk, mblk_t * mp)
{
	sl_retrieved_message(lk, mp);
}
static void sl_retrieval_complete_ind(lk_t * lk, mblk_t * mp)
{
	freemsg(mp);
	sl_retrieval_complete(lk);
}
static void sl_retrieval_not_possible_ind(lk_t * lk, mblk_t * mp)
{
	freemsg(mp);
	sl_retrieval_not_possible(lk);
}
static void sl_rb_cleared_ind(lk_t * lk, mblk_t * mp)
{
	freemsg(mp);
	sl_rb_cleared(lk);
}
static void sl_bsnt_ind(lk_t * lk, mblk_t * mp)
{
	sl_prim_t *p = (sl_prim_t *) mp->b_rptr;
	int bsnt = p->bsnt_ind.sl_bsnt;
	freemsg(mp);
	sl_bsnt(lk, bsnt);
}
static void sl_bsnt_not_retrievable_ind(lk_t * lk, mblk_t * mp)
{
	freemsg(mp);
	sl_bsnt_not_retrievable(lk);
}
static void sl_in_service_ind(lk_t * lk, mblk_t * mp)
{
	freemsg(mp);
	sl_in_service(lk);
}
static void sl_out_of_service_ind(lk_t * lk, mblk_t * mp)
{
	sl_prim_t *p = (sl_prim_t *) mp->b_rptr;
	int reason = p->out_of_service_ind.sl_reason;
	freemsg(mp);
	sl_out_of_service(lk, reason);
}
static void sl_remote_processor_outage_ind(lk_t * lk, mblk_t * mp)
{
	freemsg(mp);
	sl_remote_processor_outage(lk);
}
static void sl_remote_processor_recovered_ind(lk_t * lk, mblk_t * mp)
{
	freemsg(mp);
	sl_remote_processor_recovered(lk);
}
static void sl_rtb_cleared_ind(lk_t * lk, mblk_t * mp)
{
	freemsg(mp);
	sl_rtb_cleared(lk);
}

static void (*lk_sl_ops[]) (lk_t *, mblk_t *) = {
	sl_pdu_ind,		/* SL_PDU_IND */
	    sl_link_congested_ind,	/* SL_LINK_CONGESTED_IND */
	    sl_link_congestion_ceased_ind,	/* SL_LINK_CONGESTION_CEASED_IND */
	    sl_retrieved_message_ind,	/* SL_RETRIEVED_MESSAGE_IND */
	    sl_retrieval_complete_ind,	/* SL_RETRIEVAL_COMPLETE_IND */
	    sl_rb_cleared_ind,	/* SL_RB_CLEARED_IND */
	    sl_bsnt_ind,	/* SL_BSNT_IND */
	    sl_in_service_ind,	/* SL_IN_SERVICE_IND */
	    sl_out_of_service_ind,	/* SL_OUT_OF_SERVICE_IND */
	    sl_remote_processor_outage_ind,	/* SL_REMOTE_PROCESSOR_OUTAGE_IND */
	    sl_remote_processor_recovered_ind,	/* SL_REMOTE_PROCESSOR_RECOVERED_IND */
	    sl_rtb_cleared_ind,	/* SL_RTB_CLEARED_IND */
	    sl_retrieval_not_possible_ind,	/* SL_RETRIEVAL_NOT_POSSIBLE_IND */
	    sl_bsnt_not_retrievable_ind	/* SL_BSNT_NOT_RETRIEVABLE_IND */
};

/*
 *  =======================================================================
 *
 *  M_IOCTL handling
 *
 *  =======================================================================
 */

static int ls_do_ioctl(lmi_t * ls, int cmd, void *arg)
{
//  lmi_driver_t *drv;
	int nr = _IOC_NR(cmd);

	DTRACE;
	if (_IOC_TYPE(cmd) == LS_IOC_MAGIC)
		if (LS_IOC_FIRST <= nr && nr <= LS_IOC_LAST)
			if (ls_ioc_lmiops[nr])
				return ls_ioc_lmiops[nr] ((ls_t *) ls, cmd, arg);

//  if ( (drv = ls->driver) )
//      return drv->ops.lmi.ioctl(ls, cmd, arg);

	/* 
	 *  NOTE: If we are a multiplexor rather than a driver, we will strip the
	 *  first ls_long from the data part of the IOCTL and use that as the
	 *  signalling link code.  The remainder will be passed to that signalling
	 *  link.  Thus we can send IOCTLs for the signalling link from above.
	 *  Some of the link set IOCTLs must also configure signalling link
	 *  parameters.  Perhaps all the lower level IOCTLs can be rationalized
	 *  into an upper IOCTL.  Consider M_CTL for this.
	 */

	return -ENXIO;
}

#ifndef abs
#define abs(x) ((x)<0 ? -(x):(x))
#endif

static inline int ls_m_ioctl(queue_t * q, mblk_t * mp)
{
	lk_t *lk, **lkp;
	ls_t *ls = (ls_t *) q->q_ptr;
	struct iocblk *iocp = (struct iocblk *) mp->b_rptr;
	void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL;
	int cmd = iocp->ioc_cmd, count = iocp->ioc_count;
	int ret = -EINVAL;
	struct linkblk *lp = (struct linkblk *) arg;

	DTRACE;
	switch (cmd) {
	case I_LINK:
	case I_PLINK:
		/* 
		 *  NOTE: PLINK can be a bit different an could be rather useful
		 *  for SS7.  A PLINKED signalling link will never be closed, even
		 *  if the configuration daemon crashes, even if the signalling
		 *  link set is closed.  This permits more permanent SS7
		 *  configuration structures.  The ss7d upon restart reopens the
		 *  controll channel with root priviledges and can access the
		 *  existing configuration.
		 *
		 *  NOTE: do a MOD_INC_USE_COUNT when PLINKs are added and a
		 *  MOD_DEC_USE_COUNT when they are removed.  Otherwise the module
		 *  may autoclean.
		 */
		if (MINOR(ls->devnum)) {
			ret = -EPERM;
			break;
		}
		if (!(lk = kmalloc(sizeof(*lk), GFP_KERNEL))) {
			ret = -ENOMEM;
			break;
		}
		bzero(lk, sizeof(*lk));

		lk->next = ls->device;
		ls->device = lk;
		lk->module = ls;
		lk->ucalls = NULL;
		lk->dcalls = NULL;
		lk->device = NULL;
		lk->driver = NULL;
		lk->muxid = lp->l_index;
		lk->rq = RD(lp->l_qbot);
		lk->rq->q_ptr = lk;
		lk->wq = WR(lp->l_qbot);
		lk->wq->q_ptr = lk;

		ret = 0;
		break;
	case I_UNLINK:
	case I_PUNLINK:
		if (MINOR(ls->devnum)) {
			ret = -EPERM;
			break;
		}
		for (lkp = &ls->device; *lkp && (*lkp)->muxid != lp->l_index; lkp = &(*lkp)->next);
		if (!(lk = *lkp)) {
			ret = -ENXIO;
			break;
		}
		*lkp = lk->next;

		lk->rq->q_ptr = NULL;
		lk->wq->q_ptr = NULL;

		kfree(lk);

		ret = 0;
		break;
	default:
		if (count >= _IOC_SIZE(cmd)) {
			ret = ls_do_ioctl((lmi_t *) ls, cmd, arg);
		}
#if 0
		/* 
		 *  FIXME:
		 *
		 *  If the IOCTL is destined for the link instead of the linkset,
		 *  we should pass the command down to the link.  The link can
		 *  then attempt to deal with it.  We should use the muxid to
		 *  determine which link to pass the command to.  The muxid should
		 *  be the first element in arg.  Go through the lower mux list
		 *  at ls->device.
		 */
		if (abs(ret) == ENXIO) {
			if (q->q_next) {
				putnext(q, mp);
				return (0);
			}
		}
#endif
		break;
	}
	if (ret == 0) {
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval = 0;
	} else {
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = abs(ret);
		iocp->ioc_rval = -1;
	}
	qreply(q, mp);
	return (0);
}

/*
 *  =======================================================================
 *
 *  M_PROTO, M_PCPROTO handling
 *
 *  =======================================================================
 */

static inline int ls_m_proto(queue_t * q, mblk_t * mp)
{
	int err = LMI_BADPRIM + EOPNOTSUPP;
	ls_t *ls = (ls_t *) q->q_ptr;
	int prim = *((ls_long *) mp->b_rptr);

	DTRACE;
	DPRINT(0, ("%s: [%s %d] primitive = %d\n", __FUNCTION__, __FILE__, __LINE__, prim));
	if (LS_DSTR_FIRST <= prim && prim <= LS_DSTR_LAST) {
		(*ls_rt_ops[prim - LS_DSTR_FIRST]) (ls, mp);
		return (0);
	}
	if (q->q_next) {
		putnext(q, mp);
		return (0);
	}
	if (LMI_DSTR_FIRST <= prim && prim <= LMI_DSTR_LAST) {
		err = (*lmi_lmi_ops[prim - LMI_DSTR_FIRST]) ((lmi_t *) ls, mp);
	}
	return err;
}

static inline int lk_m_proto(queue_t * q, mblk_t * mp)
{
	int err = LMI_BADPRIM + EOPNOTSUPP;
	lk_t *lk = (lk_t *) q->q_ptr;
	int prim = *((sl_long *) mp->b_rptr);

	DTRACE;
	DPRINT(0, ("%s: [%s %d] primitive = %d\n", __FUNCTION__, __FILE__, __LINE__, prim));
	if (SL_USTR_FIRST <= prim && prim <= SL_USTR_LAST) {
		(*lk_sl_ops[SL_USTR_LAST - prim]) (lk, mp);
		return (0);
	}
	if (q->q_next) {
		putnext(q, mp);
		return (0);
	}
	return err;
}

/*
 *  =======================================================================
 *
 *  M_DATA handling
 *
 *  =======================================================================
 */

static inline int lk_m_data(queue_t * q, mblk_t * mp)
{
	lk_t *lk = (lk_t *) q->q_ptr;
	DTRACE;
	sl_pdu(lk, mp);
	return (0);
}

/*
 *  =======================================================================
 *
 *  STREAMS QUEUE PUT and QUEUE SERVICE routines
 *
 *  =======================================================================
 */

/*
 *  ------------------------------------------
 *
 *  UPPER QUEUE PUT and QUEUE SERVICE routines
 *
 *  ------------------------------------------
 */

static void ls_wput(queue_t * q, mblk_t * mp)
{
	int err = EOPNOTSUPP;
	DTRACE;
	if (q->q_count && mp->b_datap->db_type < QPCTL) {
		putq(q, mp);
		return;
	}
	switch (mp->b_datap->db_type) {
	case M_DATA:
		freemsg(mp);	/* don't support raw read/write here */
		return;
	case M_CTL:
	case M_PROTO:
	case M_PCPROTO:
		if ((err = ls_m_proto(q, mp)))
			break;
		return;
	case M_FLUSH:
		if (*mp->b_rptr & FLUSHW) {
			flushq(q, FLUSHALL);
			if (q->q_next) {
				putnext(q, mp);	/* flush all the way down */
				return;
			}
			*mp->b_rptr &= ~FLUSHW;
		}
		if (*mp->b_rptr & FLUSHR) {
			flushq(RD(q), FLUSHALL);
			qreply(q, mp);
		} else
			break;
		return;
	case M_IOCTL:
		if ((err = ls_m_ioctl(q, mp)))
			break;
		return;
	}
	switch (err & 0xffff) {
	case EAGAIN:
		putq(q, mp);
		return;
	case EOPNOTSUPP:
		if (q->q_next) {
			putnext(q, mp);
			return;
		}
	}
	DTRACE;
	freemsg(mp);		/* don't even want to complain */
	return;
}

static void ls_wsrv(queue_t * q)
{
	int err = EOPNOTSUPP;
	mblk_t *mp;

	DTRACE;
	while ((mp = getq(q))) {
		if (q->q_next && mp->b_datap->db_type < QPCTL && !canputnext(q)) {
			putbq(q, mp);
			return;
		}
		switch (mp->b_datap->db_type) {
		case M_DATA:
			freemsg(mp);	/* don't support raw read/write here */
			continue;
		case M_CTL:
		case M_PROTO:
		case M_PCPROTO:
			if ((err = ls_m_proto(q, mp)))
				break;
			continue;
		}
		switch (err & 0xffff) {
		case EAGAIN:
			if (mp->b_datap->db_type < QPCTL) {
				putbq(q, mp);
				return;
			}
			break;
		case EOPNOTSUPP:
			if (q->q_next) {
				putnext(q, mp);
				return;
			}
		}
		DTRACE;
		freemsg(mp);
	}
}

static void ls_rput(queue_t * q, mblk_t * mp)
{
	DTRACE;

	if (mp->b_datap->db_type < QPCTL && (q->q_count || !canputnext(q)))
		putq(q, mp);
	else
		putnext(q, mp);
}

static void ls_rsrv(queue_t * q)
{
	mblk_t *mp;
	DTRACE;

	while ((mp = getq(q))) {
		if (mp->b_datap->db_type < QPCTL && !canputnext(q)) {
			putbq(q, mp);
			return;
		}
		putnext(q, mp);
	}
}

/*
 *  ------------------------------------------
 *
 *  LOWER QUEUE PUT and QUEUE SERVICE routines
 *
 *  ------------------------------------------
 */

static void lk_wput(queue_t * q, mblk_t * mp)
{
	DTRACE;

	if (mp->b_datap->db_type < QPCTL && (q->q_count || !canputnext(q)))
		putq(q, mp);
	else
		putnext(q, mp);
}

static void lk_wsrv(queue_t * q)
{
	mblk_t *mp;
	DTRACE;

	while ((mp = getq(q))) {
		if (mp->b_datap->db_type < QPCTL && !canputnext(q)) {
			putbq(q, mp);
			return;
		}
		putnext(q, mp);
	}
}

static void lk_rput(queue_t * q, mblk_t * mp)
{
	int err = EOPNOTSUPP;
	queue_t *upperq = ((lk_t *) q->q_ptr)->module->rq;

	DTRACE;
	if (q->q_count && mp->b_datap->db_type < QPCTL) {
		putq(q, mp);
		return;
	}
	switch (mp->b_datap->db_type) {
	case M_DATA:
		if ((err = lk_m_data(q, mp)))
			break;
		return;
	case M_CTL:
	case M_PROTO:
	case M_PCPROTO:
		if ((err = lk_m_proto(q, mp)))
			break;
		return;
	}
	switch (err & 0xffff) {
	case EAGAIN:
		putq(q, mp);
		return;
	case EOPNOTSUPP:
		putq(upperq, mp);
		return;
	}
	DTRACE;
	freemsg(mp);
}

static void lk_rsrv(queue_t * q)
{
	mblk_t *mp;
	int err = EOPNOTSUPP;
	queue_t *upperq = ((lk_t *) q->q_ptr)->module->rq;

	DTRACE;
	while ((mp = getq(q))) {
		if (mp->b_datap->db_type < QPCTL && !canput(upperq)) {
			putbq(q, mp);
			return;
		}
		switch (mp->b_datap->db_type) {
		case M_DATA:
			if ((err = lk_m_data(q, mp)))
				break;
			continue;
		case M_CTL:
		case M_PROTO:
		case M_PCPROTO:
			if ((err = lk_m_proto(q, mp)))
				break;
			continue;
		}
		switch (err & 0xffff) {
		case EAGAIN:
			if (mp->b_datap->db_type < QPCTL) {
				putbq(q, mp);
				return;
			}
			break;
		case EOPNOTSUPP:
			putq(upperq, mp);
			return;
		}
		DTRACE;
		freemsg(mp);
	}
}

/*
 *  =======================================================================
 *
 *  OPEN and CLOSE
 *
 *  =======================================================================
 */

static lmi_driver_t *ls_drivers = NULL;

static int ls_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	int err, i;
	ls_t *ls;
	DTRACE;
	if (q->q_ptr != NULL)
		return (0);

	if ((err = lmi_mux_open(q, devp, flag, sflag, crp, ls_drivers, sizeof(ls_t))))
		return (err);
	ls = (ls_t *) q->q_ptr;
	ls->ucalls = &ls_mux_ucalls;
	ls->dcalls = &ls_mux_dcalls;

	/* FIXME: Initialize some of the ls structure here... */
	for (i = 0; i < LS_MAX_LINKS; i++) {
		bufq_init(&ls->llinks[i].cob_buf);
		bufq_init(&ls->llinks[i].rtr_buf);
	}
	return (0);
}

static int ls_close(queue_t * q, int flag, cred_t * crp)
{
	int err;
	ls_t *ls = (ls_t *) q->q_ptr;

	DTRACE;
	err = lmi_mux_close(q, flag, crp,
			    (ls_ulong *) & ls->timers, LS_MAX_TIMERIDS,
			    (ls_ulong *) & ls->bufids, LS_MAX_BUFCALLS);
	return (err);
}

/*
 *  =======================================================================
 *
 *  DRIVER Registration (driver registering with us)
 *
 *  =======================================================================
 */

int ls_register_driver(major_t cmajor, int nminor, char *name, lmi_ops_t * ops,
		       ls_dcalls_t * dcalls)
{
	int ret;

	DTRACE;
	MOD_INC_USE_COUNT;
	if ((ret = lmi_register_driver(&ls_drivers, cmajor, &ls_info, nminor, name,
				       ops, dcalls, &ls_mux_ucalls)) < 0) {
		MOD_DEC_USE_COUNT;
		return (ret);
	}
	return (ret);
}

int ls_unregister_driver(major_t cmajor)
{
	int err = lmi_unregister_driver(&ls_drivers, cmajor);
	DTRACE;
	if (!err)
		MOD_DEC_USE_COUNT;
	return (err);
}

/*
 *  =======================================================================
 *
 *  LiS Module Initialization
 *
 *  =======================================================================
 */

static int ls_initialized = 0;

#ifndef LIS_REGISTERED
static inline void ls_init(void)
#else
__initfunc(void ls_init(void))
#endif
{
	if (ls_initialized)
		return;
	ls_initialized = 1;
	printk(KERN_INFO LS_BANNER);	/* console splash */
#ifndef LIS_REGISTERED
	if (lis_register_strdev(LS_CMAJOR, &ls_info, LS_NMINOR, ls_minfo.mi_idname) < 0) {
		cmn_err(CE_NOTE, "ls: couldn't register module\n");
		ls_minfo.mi_idnum = 0;
	}
	ls_minfo.mi_idnum = 1;
#endif
#if 0
	if (mtp_register_driver(LS_CMAJOR, LS_NMINOR, "sls", &ls_lmi_ops, &ls_drv_ops)) {
		cmn_err(CE_NOTE, "sls: couldn't register as driver\n");
	}
#endif
};

#ifndef LIS_REGISTERED
static inline void ls_terminate(void)
#else
__initfunc(void ls_terminate(void))
#endif
{
	if (!ls_initialized)
		return;
	ls_initialized = 0;
#ifndef LIS_REGSITERED
	if (ls_minfo.mi_idnum)
		if (lis_unregister_strdev(ls_minfo.mi_idnum) < 0) {
			cmn_err(CE_WARN, "ls: couldn't unregister module!\n");
		}
#endif
};

/*
 *  =======================================================================
 *
 *  Kernel Module Initialization
 *
 *  =======================================================================
 */

#ifdef MODULE
int init_module(void)
{
	DTRACE;
	ls_init();
	return (0);
}

void cleanup_module(void)
{
	DTRACE;
	ls_terminate();
	return;
}
#endif


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

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

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