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/ss7sctp/sdl_sctp.c


File /code/strss7/drivers/ss7sctp/sdl_sctp.c



#ident "@(#) $RCSfile: sdl_sctp.c,v $ $Name:  $($Revision: 0.8.2.9 $) $Date: 2003/04/14 12:13:30 $"

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

#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 <sys/dki.h>

#include <sys/npi.h>
#include <sys/npi_sctp.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 "../lock.h"
#include "../debug.h"
#include "../bufq.h"

#define SDL_DESCRIP	"SS7/SCTP SIGNALLING DATA LINK (SDL) STREAMS MODULE."
#define SDL_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  All Rights Reserved."
#define SDL_DEVICE	"Part of the OpenSS7 Stack for LiS STREAMS."
#define SDL_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define SDL_LICENSE	"GPL"
#define SDL_BANNER	SDL_DESCRIP	"\n" \
			SDL_COPYRIGHT	"\n" \
			SDL_DEVICE	"\n" \
			SDL_CONTACT	"\n"

MODULE_AUTHOR(SDL_CONTACT);
MODULE_DESCRIPTION(SDL_DESCRIP);
MODULE_SUPPORTED_DEVICE(SDL_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(SDL_LICENSE);
#endif

//#define SDL_RX_COMPRESSION
//#define SDL_TX_COMPRESSION

typedef void (*bufcall_fnc_t) (long);

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */
static struct module_info sdl_minfo = {
	mi_idnum:0,			/* Module ID number */
	mi_idname:"sdl-sctp",		/* Module name */
	mi_minpsz:0,			/* Min packet size accepted *//* FIXME */
	mi_maxpsz:INFPSZ,		/* Max packet size accepted *//* FIXME */
	mi_hiwat:1 << 15,		/* Hi water mark *//* FIXME */
	mi_lowat:1 << 10,		/* Lo water mark *//* FIXME */
};

static int sdl_open(queue_t *, dev_t *, int, int, cred_t *);
static int sdl_close(queue_t *, int, cred_t *);

static int sdl_rput(queue_t *, mblk_t *);
static int sdl_rsrv(queue_t *);

static struct qinit sdl_rinit = {
	qi_putp:sdl_rput,		/* Read put (msg from below) */
	qi_srvp:sdl_rsrv,		/* Read queue service */
	qi_qopen:sdl_open,		/* Each open */
	qi_qclose:sdl_close,		/* Last close */
	qi_minfo:&sdl_minfo,		/* Information */
};

static int sdl_wput(queue_t *, mblk_t *);
static int sdl_wsrv(queue_t *);

static struct qinit sdl_winit = {
	qi_putp:sdl_wput,		/* Write put (msg from above) */
	qi_srvp:sdl_wsrv,		/* Write queue service */
	qi_minfo:&sdl_minfo,		/* Information */
};

static struct streamtab sdl_info = {
	st_rdinit:&sdl_rinit,		/* Upper read queue */
	st_wrinit:&sdl_winit,		/* Upper write queue */
};

/*
 *  =========================================================================
 *
 *  SDL Private Structure
 *
 *  =========================================================================
 */
typedef struct sdl {
	struct sdl *next;
	queue_t *rq;
	queue_t *wq;
	lis_spin_lock_t lock;
	queue_t *rwait;
	queue_t *wwait;
	uint state;			/* SDL interface state */
	uint version;			/* version executing */
	uint flags;			/* interface flags */
	ulong token;			/* my bind token */

	mblk_t *rx_buf;			/* RX compression buffer */
	size_t rx_count;		/* RX compression count */
	mblk_t *tx_buf;			/* TX compression buffer */
	size_t tx_count;		/* TX compression count */

	uint timer_tick;		/* tick timer */

	lmi_option_t lmi_conf;		/* protocol and variant options */
	sdl_config_t sdl_conf;		/* SDL configuration options */
	dev_device_t dev_conf;		/* DEV configuration options */
} sdl_t;

#define SDL_PRIV(__q) ((sdl_t *)(__q)->q_ptr)

/*
 *  =========================================================================
 *
 *  Locking
 *
 *  =========================================================================
 */
static inline int sdl_trylockq(queue_t * q)
{
	int res;
	sdl_t *s = SDL_PRIV(q);
	if (!(res = lis_spin_trylock(&s->lock))) {
		if (q == s->rq)
			s->rwait = q;
		if (q == s->wq)
			s->wwait = q;
	}
	return (res);
}
static inline int sdl_unlockq(queue_t * q)
{
	sdl_t *s = SDL_PRIV(q);
	lis_spin_unlock(&s->lock);
	if (s->rwait)
		qenable(xchg(&s->rwait, NULL));
	if (s->wwait)
		qenable(xchg(&s->wwait, NULL));
}

#define SDL_LSSU_SIO		0
#define SDL_LSSU_SIN		1
#define SDL_LSSU_SIE		2
#define SDL_LSSU_SIOS		3
#define SDL_LSSU_SIPO		4
#define SDL_LSSU_SIB		5
#define SDL_LSS2_SIO		0 + 8
#define SDL_LSS2_SIN		1 + 8
#define SDL_LSS2_SIE		2 + 8
#define SDL_LSS2_SIOS		3 + 8
#define SDL_LSS2_SIPO		4 + 8
#define SDL_LSS2_SIB		5 + 8
#define SDL_FISU		16
#define SDL_MSU			17

#define SDL_FLAG_RX_ENABLED	0x01
#define SDL_FLAG_TX_ENABLED	0x02

#define SDL_EVENT_TX_WAKEUP	1

/*
 *  =========================================================================
 *
 *  SDL Provider (SDL) -> SDL User Primitives
 *
 *  =========================================================================
 */
/*
 *  LMI_INFO_ACK
 *  ---------------------------------------------
 */
static int lmi_info_ack(sdl_t * sp)
{
	mblk_t *mp;
	lmi_info_ack_t *p;
	ensure(sp, return (-EFAULT));
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_info_ack_t *) mp->b_wptr)++;
		p->lmi_primitive = LMI_INFO_ACK;
		p->lmi_version = 1;
		p->lmi_state = sp->state;
		p->lmi_max_sdu = -1;
		p->lmi_min_sdu = 0;
		p->lmi_header_len = 0;
		p->lmi_ppa_style = LMI_STYLE1;
		putnext(sp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

#if 0
/*
 *  LMI_OK_ACK
 *  ---------------------------------------------
 */
static int lmi_ok_ack(sdl_t * sp, long prim)
{
	mblk_t *mp;
	lmi_ok_ack_t *p;
	ensure(sp, return (-EFAULT));
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_ok_ack_t *) mp->b_wptr)++;
		p->lmi_primitive = LMI_OK_ACK;
		p->lmi_correct_primitive = prim;
		switch (sp->state) {
		case LMI_ATTACH_PENDING:
			sp->state = LMI_DISABLED;
			break;
		case LMI_DETACH_PENDING:
			sp->state = LMI_UNATTACHED;
			break;
			/* default is don't change state */
		}
		p->lmi_state = sp->state;
		putnext(sp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/*
 *  LMI_ERROR_ACK
 *  ---------------------------------------------
 */
static int lmi_error_ack(sdl_t * sp, long prim, long err)
{
	mblk_t *mp;
	lmi_error_ack_t *p;
	switch (err) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (err);
	}
	ensure(sp, return (-EFAULT));
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_error_ack_t *) mp->b_wptr)++;
		p->lmi_primitive = LMI_ERROR_ACK;
		p->lmi_errno = err < 0 ? -err : 0;
		p->lmi_reason = err < 0 ? LMI_SYSERR : err;
		p->lmi_error_primitive = prim;
		switch (sp->state) {
		case LMI_ATTACH_PENDING:
			sp->state = LMI_UNATTACHED;
			break;
		case LMI_DETACH_PENDING:
			sp->state = LMI_DISABLED;
			break;
		case LMI_ENABLE_PENDING:
			sp->state = LMI_DISABLED;
			break;
		case LMI_DISABLE_PENDING:
			sp->state = LMI_ENABLED;
			break;
			/* 
			 *  Default is not to change state.
			 */
		}
		p->lmi_state = sp->state;
		putnext(sp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  LMI_ENABLE_CON
 *  ---------------------------------------------
 */
static int lmi_enable_con(sdl_t * sp)
{
	mblk_t *mp;
	lmi_enable_con_t *p;
	ensure(sp, return (-EFAULT));
	ensure(sp->state == LMI_ENABLE_PENDING, return (-EFAULT));
	if (canputnext(sp->rq)) {
		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((lmi_enable_con_t *) mp->b_wptr)++;
			p->lmi_primitive = LMI_ENABLE_CON;
			p->lmi_state = sp->state = LMI_ENABLED;
			putnext(sp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}

/*
 *  LMI_DISABLE_CON
 *  ---------------------------------------------
 */
static int lmi_disable_con(sdl_t * sp)
{
	mblk_t *mp;
	lmi_disable_con_t *p;
	ensure(sp, return (-EFAULT));
	ensure(sp->state == LMI_DISABLE_PENDING, return (-EFAULT));
	if (canputnext(sp->rq)) {
		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((lmi_disable_con_t *) mp->b_wptr)++;
			p->lmi_primitive = LMI_DISABLE_CON;
			p->lmi_state = sp->state = LMI_DISABLED;
			putnext(sp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}

#if 0
/*
 *  LMI_OPTMGMT_ACK
 *  ---------------------------------------------
 */
static int lmi_optmgmt_ack(sdl_t * sp, ulong flags, void *opt_ptr, size_t opt_len)
{
	mblk_t *mp;
	lmi_optmgmt_ack_t *p;
	ensure(sp, return (-EFAULT));
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_optmgmt_ack_t *) mp->b_wptr)++;
		p->lmi_primitive = LMI_OPTMGMT_ACK;
		p->lmi_opt_length = opt_len;
		p->lmi_opt_offset = opt_len ? sizeof(*p) : 0;
		p->lmi_mgmt_flags = flags;
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
		putnext(sp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/*
 *  LMI_ERROR_IND
 *  ---------------------------------------------
 */
#if 0
static int lmi_error_ind(sdl_t * sp, long err)
{
	mblk_t *mp;
	lmi_error_ind_t *p;
	ensure(sp, return (-EFAULT));
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_error_ind_t *) mp->b_wptr)++;
		p->lmi_primitive = LMI_ERROR_IND;
		p->lmi_errno = err < 0 ? -err : 0;
		p->lmi_reason = err < 0 ? LMI_SYSERR : err;
		p->lmi_state = sp->state;
		putnext(sp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/*
 *  LMI_STATS_IND
 *  ---------------------------------------------
 */
#if 0
static int lmi_stats_ind(sdl_t * sp, ulong interval, ulong timestamp)
{
	mblk_t *mp;
	lmi_stats_ind_t *p;
	ensure(sp, return (-EFAULT));
	if (canputnext(sp->rq)) {
		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((lmi_stats_ind_t *) mp->b_wptr)++;
			p->lmi_primitive = LMI_STATS_IND;
			p->lmi_interval = interval;
			p->lmi_timestamp = timestamp;
			putnext(sp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}
#endif
/*
 *  LMI_EVENT_IND
 *  ---------------------------------------------
 */
#if 0
static int lmi_event_ind(sdl_t * sp, ulong oid, ulong severity, ulong timestamp)
{
	mblk_t *mp;
	lmi_event_ind_t *p;
	ensure(sp, return (-EFAULT));
	if (canputnext(sp->rq)) {
		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((lmi_event_ind_t *) mp->b_wptr)++;
			p->lmi_primitive = LMI_EVENT_IND;
			p->lmi_objectid = oid;
			p->lmi_timestamp = timestamp;
			p->lmi_severity = severity;
			putnext(sp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}
#endif
/*
 *  SDL_DAEDR_RECEIVED_BITS_IND
 *  ---------------------------------------------
 */
static int sdl_read(sdl_t * sp, mblk_t * dp)
{
	ensure(sp, return (-EFAULT));
	if (canputnext(sp->rq)) {
		putnext(sp->rq, dp);
		return (0);
	}
	seldom();
	return (-EBUSY);
}

#if 0
static int sdl_daedr_received_bits_ind(sdl_t * sp, ulong count, mblk_t * dp)
{
	mblk_t *mp;
	sdl_daedr_received_bits_ind_t *p;
	ensure(sp, return (-EFAULT));
	if (canputnext(sp->rq)) {
		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((sdl_daedr_received_bits_ind_t *) mp->b_wptr)++;
			p->sdl_primitive = SDL_DAEDR_RECEIVED_BITS_IND;
			p->sdl_count = count;
			putnext(sp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}
#endif
/*
 *  SDL_DAEDR_CORRECT_SU_IND
 *  ---------------------------------------------
 */
#if 0
static int sdl_daedr_correct_su_ind(sdl_t * sp, ulong count)
{
	mblk_t *mp;
	sdl_daedr_correct_su_ind_t *p;
	ensure(sp, return (-EFAULT));
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((sdl_daedr_correct_su_ind_t *) mp->b_wptr)++;
		p->sdl_primitive = SDL_DAEDR_CORRECT_SU_IND;
		p->sdl_count = count;
		putnext(sp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/*
 *  SDL_DAEDR_SU_IN_ERROR_IND
 *  ---------------------------------------------
 */
#if 0
static int sdl_daedr_su_in_error_ind(sdl_t * sp)
{
	mblk_t *mp;
	sdl_daedr_su_in_error_ind_t *p;
	ensure(sp, return (-EFAULT));
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((sdl_daedr_su_in_error_ind_t *) mp->b_wptr)++;
		p->sdl_primitive = SDL_DAEDR_SU_IN_ERROR_IND;
		putnext(sp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/*
 *  SDL_DAEDT_TRANSMISSION_REQUEST_IND
 *  ---------------------------------------------
 */
static int sdl_daedt_transmission_request_ind(sdl_t * sp)
{
	mblk_t *mp;
	sdl_daedt_transmission_request_ind_t *p;
	ensure(sp, return (-EFAULT));
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((sdl_daedt_transmission_request_ind_t *) mp->b_wptr)++;
		p->sdl_primitive = SDL_DAEDT_TRANSMISSION_REQUEST_IND;
		putnext(sp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  =========================================================================
 *
 *  NPI User (SDL) -> NPI Provider (SCTP) Primitives
 *
 *  =========================================================================
 */
/*
 *  N_DATA_REQ
 *  ---------------------------------------------
 */
static int n_data_req(sdl_t * sp, ulong flags, void *qos_ptr, size_t qos_len, mblk_t * dp)
{
	mblk_t *mp;
	N_data_req_t *p;
	ensure(sp, return (-EFAULT));
	ensure(dp, return (-EFAULT));
	if (canputnext(sp->wq)) {
		if ((mp = allocb(sizeof(*p) + qos_len, BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((N_data_req_t *) mp->b_wptr)++;
			p->PRIM_type = N_DATA_REQ;
			p->DATA_xfer_flags = flags;
			bcopy(qos_ptr, mp->b_wptr, qos_len);
			mp->b_wptr += qos_len;
			mp->b_cont = dp;
			putnext(sp->wq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}

/*
 *  N_EXDATA_REQ
 *  ---------------------------------------------
 */
#if 0
static int n_exdata_req(sdl_t * sp, void *qos_ptr, size_t qos_len, mblk_t * dp)
{
	mblk_t *mp;
	N_exdata_req_t *p;
	ensure(sp, return (-EFAULT));
	ensure(dp, return (-EFAULT));
	if (bcanputnext(sp->wq, 1)) {
		if ((mp = allocb(sizeof(*p) + qos_len, BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((N_exdata_req_t *) mp->b_wptr)++;
			p->PRIM_type = N_EXDATA_REQ;
			bcopy(qos_ptr, mp->b_wptr, qos_len);
			mp->b_wptr += qos_len;
			mp->b_cont = dp;
			putnext(sp->wq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}
#endif

/*
 *  =========================================================================
 *
 *  SDL PDU Message Functions
 *
 *  =========================================================================
 */

#define SDL_PPI	    10

static int sdl_write(sdl_t * sp, mblk_t * mp)
{
	int err;
	uint xsn = sp->lmi_conf.popt & SS7_POPT_XSN;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, SDL_PPI, 0, 0, 0, 0 };

	if ((err = n_data_req(sp, 0, &qos, sizeof(qos), mp)))
		return (err);
	if ((!xsn && mlen > 5) || (xsn && mlen > 8))
		sdl_daedt_transmission_request_ind(sp);
	return (0);
}

/*
 *  =========================================================================
 *
 *  SDL Provider (SDL) <- SDL User Primitives
 *
 *  =========================================================================
 */
/*
 *  LMI_INFO_REQ
 *  ---------------------------------------------
 */
static int lmi_info_req(sdl_t * sp, mblk_t * mp)
{
	(void) mp;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	return lmi_info_ack(sp);
}

/*
 *  LMI_ATTACH_REQ
 *  ---------------------------------------------
 */
static int lmi_attach_req(sdl_t * sp, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_attach_req_t *p = (lmi_attach_req_t *) mp->b_rptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	do {
		if (mlen >= sizeof(*p)) {
			if (sp->state == LMI_UNATTACHED) {
				sp->state = LMI_ATTACH_PENDING;

				lmi_error_ack(sp, LMI_ATTACH_REQ, LMI_NOTSUPP);

			}
			seldom();
			err = LMI_OUTSTATE;
			break;	/* would place interface out of state */
		}
		seldom();
		err = LMI_PROTOSHORT;
		break;		/* M_PROTO block too short */
	} while (0);
	seldom();
	return lmi_error_ack(sp, LMI_ATTACH_REQ, err);
}

/*
 *  LMI_DETACH_REQ
 *  ---------------------------------------------
 */
static int lmi_detach_req(sdl_t * sp, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_detach_req_t *p = (lmi_detach_req_t *) mp->b_rptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	do {
		if (mlen >= sizeof(*p)) {
			if (sp->state == LMI_DISABLED) {
				sp->state = LMI_DETACH_PENDING;

				lmi_error_ack(sp, LMI_DETACH_REQ, LMI_NOTSUPP);

			}
			seldom();
			err = LMI_OUTSTATE;
			break;	/* would place interface out of state */
		}
		seldom();
		err = LMI_PROTOSHORT;
		break;		/* M_PROTO block too short */
	} while (0);
	seldom();
	return lmi_error_ack(sp, LMI_DETACH_REQ, err);
}

/*
 *  LMI_ENABLE_REQ
 *  ---------------------------------------------
 */
static int lmi_enable_req(sdl_t * sp, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_enable_req_t *p = (lmi_enable_req_t *) mp->b_rptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	do {
		if (mlen >= sizeof(*p)) {
			if (sp->state == LMI_DISABLED) {
				sp->state = LMI_ENABLE_PENDING;

				return lmi_enable_con(sp);

			}
			seldom();
			err = LMI_OUTSTATE;
			break;	/* would place interface out of state */
		}
		seldom();
		err = LMI_PROTOSHORT;
		break;		/* M_PROTO block too short */
	} while (0);
	seldom();
	return lmi_error_ack(sp, LMI_ENABLE_REQ, err);
}

/*
 *  LMI_DISABLE_REQ
 *  ---------------------------------------------
 */
static int lmi_disable_req(sdl_t * sp, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_disable_req_t *p = (lmi_disable_req_t *) mp->b_rptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	do {
		if (mlen >= sizeof(*p)) {
			if (sp->state == LMI_ENABLED || sp->state == LMI_ENABLE_PENDING) {

				sp->state = LMI_DISABLE_PENDING;

				freemsg(xchg(&sp->rx_buf, NULL));
				sp->rx_count = 0;
				freemsg(xchg(&sp->tx_buf, NULL));
				sp->tx_count = 0;

				return lmi_disable_con(sp);

			}
			seldom();
			err = LMI_OUTSTATE;
			break;	/* would place interface out of state */
		}
		seldom();
		err = LMI_PROTOSHORT;
		break;		/* M_PROTO block too short */
	} while (0);
	seldom();
	return lmi_error_ack(sp, LMI_DISABLE_REQ, err);
}

/*
 *  LMI_OPTMGMT_REQ
 *  ---------------------------------------------
 */
static int lmi_optmgmt_req(sdl_t * sp, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_optmgmt_req_t *p = (lmi_optmgmt_req_t *) mp->b_rptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	do {
		if (mlen >= sizeof(*p)) {

			lmi_error_ack(sp, LMI_OPTMGMT_REQ, LMI_NOTSUPP);

		}
		seldom();
		err = LMI_PROTOSHORT;
		break;		/* M_PROTO block too short */
	} while (0);
	seldom();
	return lmi_error_ack(sp, LMI_OPTMGMT_REQ, err);
}

/*
 *  SDL_DAEDT_TRAMSMISSION_REQ
 *  ---------------------------------------------
 */
#ifndef abs
#define abs(x) ((x)<0?-(x):(x))
#endif
static int m_error_reply(sdl_t * sp, int err)
{
	mblk_t *mp;
	ensure(sp, return (-EFAULT));
	switch (err) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (err);
	case 0:
	case 1:
	case 2:
		never();
		return (err);
	}
	if ((mp = allocb(2, BPRI_HI))) {
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_wptr)++ = abs(err);
		*(mp->b_wptr)++ = abs(err);
		putnext(sp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
static int sdl_daedt_xmit_req(sdl_t * sp, mblk_t * mp)
{
	int err;
	mblk_t *dp;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sdl_daedt_transmission_req_t *p = (sdl_daedt_transmission_req_t *) mp->b_rptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	do {
		if ((dp = mp->b_cont)) {
			if (mlen >= sizeof(*p)) {
				if (sp->state != LMI_DISABLED) {
					if (sp->state == LMI_ENABLED) {
						if (sp->flags & SDL_FLAG_TX_ENABLED) {

							if ((err = sdl_write(sp, dp)))
								break;
							mp->b_cont = NULL;	/* absorbed data
										   portion */
							return (0);

						}
						rare();
						return (0);	/* ignore if not enabled */
					}
					seldom();
					err = LMI_OUTSTATE;
					break;	/* would place interface out of state */
				}
				seldom();
				return (0);
				break;	/* ingore data in DISABLED state */
			}
			seldom();
			err = LMI_PROTOSHORT;
			break;	/* M_PROTO block too short */
		}
		rare();
		err = -EFAULT;
		break;		/* no M_DATA blocks */
	} while (0);
	rare();
	return m_error_reply(sp, err);	/* XXX */
}

/*
 *  SDL_DAEDT_START_REQ
 *  ---------------------------------------------
 */
static int sdl_daedt_start_req(sdl_t * sp, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sdl_daedt_start_req_t *p = (sdl_daedt_start_req_t *) mp->b_rptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	do {
		if (mlen >= sizeof(*p)) {
			if (sp->state == LMI_ENABLED) {

				/* enable the transmitter section */
				sp->flags |= SDL_FLAG_TX_ENABLED;
				return (0);

			}
			seldom();
			err = LMI_OUTSTATE;
			break;	/* would place interface out of state */
		}
		seldom();
		err = LMI_PROTOSHORT;
		break;		/* M_PROTO block too short */
	} while (0);
	rare();
	return m_error_reply(sp, err);	/* XXX */
}

/*
 *  SDL_DAEDR_START_REQ
 *  ---------------------------------------------
 */
static int sdl_daedr_start_req(sdl_t * sp, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sdl_daedr_start_req_t *p = (sdl_daedr_start_req_t *) mp->b_rptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	do {
		if (mlen >= sizeof(*p)) {
			if (sp->state == LMI_ENABLED) {

				/* enable the receiver section */
				sp->flags |= SDL_FLAG_RX_ENABLED;
				return (0);

			}
			seldom();
			err = LMI_OUTSTATE;
			break;	/* would place interface out of state */
		}
		seldom();
		err = LMI_PROTOSHORT;
		break;		/* M_PROTO block too short */
	} while (0);
	rare();
	return m_error_reply(sp, err);	/* XXX */
}

/*
 *  =========================================================================
 *
 *  NPI User (SDL) <- NPI Provider (SCTP) Primitives
 *
 *  =========================================================================
 */
/*
 *  N_CONN_IND
 *  ---------------------------------------------
 */
static int n_conn_ind(sdl_t * sp, mblk_t * mp)
{
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	(void) sp;
	(void) mp;
	rare();
	return (0);
}

/*
 *  N_CONN_CON
 *  ---------------------------------------------
 */
static int n_conn_con(sdl_t * sp, mblk_t * mp)
{
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	(void) sp;
	(void) mp;
	rare();
	return (0);
}

/*
 *  N_DISCON_IND
 *  ---------------------------------------------
 */
static int n_discon_ind(sdl_t * sp, mblk_t * mp)
{
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	(void) sp;
	(void) mp;
	rare();
	return (0);
}

/*
 *  N_DATA_IND
 *  ---------------------------------------------
 */
static int n_data_ind(sdl_t * sp, mblk_t * mp)
{
	int err;
	mblk_t *dp;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_data_ind_t *p = (N_data_ind_t *) mp->b_rptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	ensure(mlen >= sizeof(*p), return (-EFAULT));
	if (mlen <= sp->sdl_conf.m) {
		if ((dp = mp->b_cont)) {
			if (sp->state == LMI_ENABLED) {
				if (sp->flags & SDL_FLAG_RX_ENABLED) {
					if (canputnext(sp->rq)) {
						if ((err = sdl_read(sp, dp)))
							return (err);
						mp->b_cont = NULL;	/* absorbed data portion */
						return (0);
					}
					seldom();
					return (-EBUSY);	/* flow controlled */
				}
				seldom();
				return (0);	/* ignore unless enabled */
			}
			rare();
			return (-EPROTO);	/* ignore data in other states */
		}
		rare();
		return (-EFAULT);
	}
	rare();
	return (-EMSGSIZE);
}

/*
 *  N_EXDATA_IND
 *  ---------------------------------------------
 */
static int n_exdata_ind(sdl_t * sp, mblk_t * mp)
{
	int err;
	mblk_t *dp;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_exdata_ind_t *p = (N_exdata_ind_t *) mp->b_rptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	ensure(mlen >= sizeof(*p), return (-EFAULT));
	if (mlen <= sp->sdl_conf.m) {
		if ((dp = mp->b_cont)) {
			if (sp->state == LMI_ENABLED) {
				if (sp->flags & SDL_FLAG_RX_ENABLED) {
					if (bcanputnext(sp->rq, 1)) {
						if ((err = sdl_read(sp, dp)))
							return (err);
						mp->b_cont = NULL;	/* absorbed data portion */
						return (0);
					}
					seldom();
					return (-EBUSY);	/* flow controlled */
				}
				seldom();
				return (0);	/* ignore unless enabled */
			}
			rare();
			return (-EPROTO);	/* ignore data in other states */
		}
		rare();
		return (-EFAULT);
	}
	rare();
	return (-EMSGSIZE);
}

/*
 *  N_DATACK_IND
 *  ---------------------------------------------
 */
static int n_datack_ind(sdl_t * sp, mblk_t * mp)
{
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	(void) sp;
	(void) mp;
	rare();
	return (0);
}

/*
 *  N_INFO_ACK
 *  ---------------------------------------------
 */
static int n_info_ack(sdl_t * sp, mblk_t * mp)
{
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	(void) sp;
	(void) mp;
	rare();
	return (0);
}

/*
 *  N_BIND_ACK
 *  ---------------------------------------------
 */
static int n_bind_ack(sdl_t * sp, mblk_t * mp)
{
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	(void) sp;
	(void) mp;
	rare();
	return (0);
}

/*
 *  N_OK_ACK
 *  ---------------------------------------------
 */
static int n_ok_ack(sdl_t * sp, mblk_t * mp)
{
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	(void) sp;
	(void) mp;
	rare();
	return (0);
}

/*
 *  N_ERROR_ACK
 *  ---------------------------------------------
 */
static int n_error_ack(sdl_t * sp, mblk_t * mp)
{
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	(void) sp;
	(void) mp;
	rare();
	return (0);
}

/*
 *  N_RESET_IND
 *  ---------------------------------------------
 */
static int n_reset_ind(sdl_t * sp, mblk_t * mp)
{
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	(void) sp;
	(void) mp;
	rare();
	return (0);
}

/*
 *  N_RESET_CON
 *  ---------------------------------------------
 */
static int n_reset_con(sdl_t * sp, mblk_t * mp)
{
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	(void) sp;
	(void) mp;
	rare();
	return (0);
}

/*
 *  =========================================================================
 *
 *  IO Controls
 *
 *  =========================================================================
 *  I have only implemented enough of these to get the Q.781 test programs
 *  running.
 */
/*
 *  SDL_IOCGOPTIONS
 *  ---------------------------------------------
 */
static int sdl_iocgoptions(queue_t * q, int cmd, void *arg)
{
	sdl_t *sp;
	lmi_option_t *opt = (lmi_option_t *) arg;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return (-EFAULT));
	ensure(arg, return (-EFAULT));
	*opt = sp->lmi_conf;
	return (0);
}

/*
 *  SDL_IOCSOPTIONS
 *  ---------------------------------------------
 */
static int sdl_iocsoptions(queue_t * q, int cmd, void *arg)
{
	sdl_t *sp;
	lmi_option_t *opt = (lmi_option_t *) arg;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return (-EFAULT));
	ensure(arg, return (-EFAULT));
	switch (opt->pvar) {
	case SS7_PVAR_ITUT_88:
	case SS7_PVAR_ITUT_93:
	case SS7_PVAR_ITUT_96:
	case SS7_PVAR_ITUT_00:
	case SS7_PVAR_ANSI_88:
	case SS7_PVAR_ANSI_92:
	case SS7_PVAR_ANSI_96:
	case SS7_PVAR_ANSI_00:
	case SS7_PVAR_ETSI_88:
	case SS7_PVAR_ETSI_93:
	case SS7_PVAR_ETSI_96:
	case SS7_PVAR_ETSI_00:
	case SS7_PVAR_JTTC_94:
		break;
	default:
		rare();
		return (-EINVAL);
	}
	if (opt->popt & ~(SS7_POPT_MPLEV | SS7_POPT_HSL | SS7_POPT_XSN)) {
		rare();
		return (-EINVAL);
	}
	sp->lmi_conf = *opt;
	return (0);
}

/*
 *  SDL_IOCGCONFIG
 *  ---------------------------------------------
 */
static int sdl_iocgconfig(queue_t * q, int cmd, void *arg)
{
	sdl_t *sp;
	sdl_config_t *cnf = (sdl_config_t *) arg;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return (-EFAULT));
	ensure(arg, return (-EFAULT));
	*cnf = sp->sdl_conf;
	return (0);
}

/*
 *  SDL_IOCSCONFIG
 *  ---------------------------------------------
 */
static int sdl_iocsconfig(queue_t * q, int cmd, void *arg)
{
	sdl_t *sp;
	sdl_config_t *cnf = (sdl_config_t *) arg;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return (-EFAULT));
	ensure(arg, return (-EFAULT));
	sp->sdl_conf = *cnf;
	return (0);
}

/*
 *  DEV_IOCSIFCLOCK
 *  ---------------------------------------------
 */
static int dev_iocsifclock(queue_t * q, int cmd, void *arg)
{
	sdl_t *sp;
	ulong *val = (ulong *) arg;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return (-EFAULT));
	ensure(arg, return (-EFAULT));
	switch (*val) {
	case DEV_CLOCK_NONE:
	case DEV_CLOCK_ABR:
	case DEV_CLOCK_SHAPER:
	case DEV_CLOCK_TICK:
		sp->dev_conf.ifclock = *val;
		return (0);

	default:
	case DEV_CLOCK_INT:
	case DEV_CLOCK_EXT:
	case DEV_CLOCK_LOOP:
	case DEV_CLOCK_MASTER:
	case DEV_CLOCK_SLAVE:
	case DEV_CLOCK_DPLL:
		break;
	}
	return (-EINVAL);
}

/*
 *  DEV_IOCGIFCLOCK
 *  ---------------------------------------------
 */
static int dev_iocgifclock(queue_t * q, int cmd, void *arg)
{
	sdl_t *sp;
	ulong *val = (ulong *) arg;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return (-EFAULT));
	ensure(arg, return (-EFAULT));
	*val = sp->dev_conf.ifclock;
	return (0);
}

/*
 *  DEV_IOCCDISCTX
 *  ---------------------------------------------
 *  Disconnect the transmit path.
 */
static int dev_ioccdisctx(queue_t * q, int cmd, void *arg)
{
	ensure(q, return (-EFAULT));
	ensure(arg, return (-EFAULT));
	return (-EOPNOTSUPP);
}

/*
 *  DEV_IOCCCONNTX
 *  ---------------------------------------------
 *  Reconnect the transmit path.
 */
static int dev_ioccconntx(queue_t * q, int cmd, void *arg)
{
	ensure(q, return (-EFAULT));
	ensure(arg, return (-EFAULT));
	return (-EOPNOTSUPP);
}

/*
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sdl_w_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	sdl_t *sp;
	ulong oldstate;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	oldstate = sp->state;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	switch ((prim = *((ulong *) mp->b_rptr))) {
	case LMI_INFO_REQ:
		rtn = lmi_info_req(sp, mp);
		break;
	case LMI_ATTACH_REQ:
		rtn = lmi_attach_req(sp, mp);
		break;
	case LMI_DETACH_REQ:
		rtn = lmi_detach_req(sp, mp);
		break;
	case LMI_ENABLE_REQ:
		rtn = lmi_enable_req(sp, mp);
		break;
	case LMI_DISABLE_REQ:
		rtn = lmi_disable_req(sp, mp);
		break;
	case LMI_OPTMGMT_REQ:
		rtn = lmi_optmgmt_req(sp, mp);
		break;
	case SDL_DAEDT_TRANSMISSION_REQ:
		rtn = sdl_daedt_xmit_req(sp, mp);
		break;
	case SDL_DAEDT_START_REQ:
		rtn = sdl_daedt_start_req(sp, mp);
		break;
	case SDL_DAEDR_START_REQ:
		rtn = sdl_daedr_start_req(sp, mp);
		break;
	default:
		rtn = -EOPNOTSUPP;
		break;
	}
	if (rtn < 0) {
		seldom();
		sp->state = oldstate;
	}
	return (rtn);
}
static int sdl_r_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	sdl_t *sp;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	switch ((prim = *((ulong *) mp->b_rptr))) {
	case N_CONN_IND:
		rtn = n_conn_ind(sp, mp);
		break;
	case N_CONN_RES:
		rtn = n_conn_con(sp, mp);
		break;
	case N_DISCON_IND:
		rtn = n_discon_ind(sp, mp);
		break;
	case N_DATA_IND:
		rtn = n_data_ind(sp, mp);
		break;
	case N_EXDATA_IND:
		rtn = n_exdata_ind(sp, mp);
		break;
	case N_DATACK_IND:
		rtn = n_datack_ind(sp, mp);
		break;
	case N_INFO_ACK:
		rtn = n_info_ack(sp, mp);
		break;
	case N_BIND_ACK:
		rtn = n_bind_ack(sp, mp);
		break;
	case N_OK_ACK:
		rtn = n_ok_ack(sp, mp);
		break;
	case N_ERROR_ACK:
		rtn = n_error_ack(sp, mp);
		break;
	case N_RESET_IND:
		rtn = n_reset_ind(sp, mp);
		break;
	case N_RESET_CON:
		rtn = n_reset_con(sp, mp);
		break;
	default:
		rtn = -EOPNOTSUPP;
		break;
	}
	if (rtn < 0) {
		seldom();
	}
	return (rtn);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sdl_w_data(queue_t * q, mblk_t * mp)
{
	int err;
	sdl_t *sp;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	if ((err = sdl_write(sp, mp)))
		return err;
	return (1);		/* absorbed mblk */
}
static int sdl_r_data(queue_t * q, mblk_t * mp)
{
	int err;
	sdl_t *sp;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	if ((err = sdl_read(sp, mp)))
		return err;
	return (1);		/* absorbed mblk */
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_CTL Handling (Events)
 *
 *  -------------------------------------------------------------------------
 */
#if 0
static int sdl_r_ctl(queue_t * q, mblk_t * mp)
{
	sdl_t *sp;
	ensure(q, return (-EFAULT));
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	switch (*mp->b_rptr) {
	case SDL_EVENT_TX_WAKEUP:
		sdl_tx_wakeup(sp);
		return (0);
	}
	return (-EOPNOTSUPP);
}
#endif
/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sdl_w_ioctl(queue_t * q, mblk_t * mp)
{
	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;
	struct linkblk *lp = (struct linkblk *) arg;
	int ret = -EINVAL;
	int type = _IOC_TYPE(cmd);
	int nr = _IOC_NR(cmd);
	int size = _IOC_SIZE(cmd);
	(void) nr;
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	switch (type) {
	case __SID:
		switch (cmd) {
		default:
			ptrace(("Unknown IOCTL %x\n", cmd));
		case I_STR:
		case I_LINK:
		case I_PLINK:
		case I_UNLINK:
		case I_PUNLINK:
			rare();
			(void) lp;
			ret = -EINVAL;
			break;
		}
		ret = -EOPNOTSUPP;
		break;
	case SDL_IOC_MAGIC:
		if (count >= size) {
			switch (cmd) {
			case SDL_IOCGOPTIONS:
				ret = sdl_iocgoptions(q, cmd, arg);
				break;
			case SDL_IOCSOPTIONS:
				ret = sdl_iocsoptions(q, cmd, arg);
				break;
			case SDL_IOCGCONFIG:
				ret = sdl_iocgconfig(q, cmd, arg);
				break;
			case SDL_IOCSCONFIG:
				ret = sdl_iocsconfig(q, cmd, arg);
				break;
			default:
			case SDL_IOCTCONFIG:
			case SDL_IOCCCONFIG:
			case SDL_IOCGSTATEM:
			case SDL_IOCCMRESET:
			case SDL_IOCGSTATSP:
			case SDL_IOCSSTATSP:
			case SDL_IOCGSTATS:
			case SDL_IOCCSTATS:
			case SDL_IOCGNOTIFY:
			case SDL_IOCSNOTIFY:
			case SDL_IOCCNOTIFY:
				ret = -EOPNOTSUPP;
				break;
			}
		} else
			ret = -EINVAL;
		break;
	case DEV_IOC_MAGIC:
		if (count >= size) {
			switch (cmd) {
			case DEV_IOCSIFCLOCK:
				ret = dev_iocsifclock(q, cmd, arg);
				break;
			case DEV_IOCGIFCLOCK:
				ret = dev_iocgifclock(q, cmd, arg);
				break;
			case DEV_IOCCDISCTX:
				ret = dev_ioccdisctx(q, cmd, arg);
				break;
			case DEV_IOCCCONNTX:
				ret = dev_ioccconntx(q, cmd, arg);
				break;
			default:
			case DEV_IOCCIFRESET:
			case DEV_IOCGIFFLAGS:
			case DEV_IOCSIFFLAGS:
			case DEV_IOCGIFTYPE:
			case DEV_IOCSIFTYPE:
			case DEV_IOCGGRPTYPE:
			case DEV_IOCSGRPTYPE:
			case DEV_IOCGIFMODE:
			case DEV_IOCSIFMODE:
			case DEV_IOCGIFRATE:
			case DEV_IOCSIFRATE:
			case DEV_IOCGIFCODING:
			case DEV_IOCSIFCODING:
			case DEV_IOCGIFLEADS:
			case DEV_IOCSIFLEADS:
			case DEV_IOCCIFLEADS:
				ret = -EOPNOTSUPP;
				break;
			}
		} else
			ret = -EINVAL;
		break;
	default:
		if (q->q_next) {
			putnext(q, mp);
			return (1);
		}
		ret = -EOPNOTSUPP;
		break;
	}
	if (ret >= 0) {
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval = 0;
	} else {
		seldom();
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = -ret;
		iocp->ioc_rval = -1;
	}
	qreply(q, mp);
	return (1);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sdl_r_error(queue_t * q, mblk_t * mp)
{
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	putnext(q, mp);
	return (1);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sdl_m_flush(queue_t * q, mblk_t * mp, const uint8_t mflag)
{
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	if (*mp->b_rptr & mflag) {
		if (*mp->b_rptr & FLUSHBAND)
			flushband(q, mp->b_rptr[1], FLUSHALL);
		else
			flushq(q, FLUSHALL);
	}
	putnext(q, mp);
	return (1);
}
static int sdl_w_flush(queue_t * q, mblk_t * mp)
{
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	return sdl_m_flush(q, mp, FLUSHW);
}
static int sdl_r_flush(queue_t * q, mblk_t * mp)
{
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	return sdl_m_flush(q, mp, FLUSHR);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Other messages (e.g. M_IOCACK)
 *
 *  -------------------------------------------------------------------------
 */
static int sdl_m_other(queue_t * q, mblk_t * mp)
{
	rare();
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	if (mp->b_datap->db_type >= QPCTL || bcanputnext(q, mp->b_band)) {
		putnext(q, mp);
		return (1);
	} else
		return (-EBUSY);
}

/*
 *  =========================================================================
 *
 *  PUT and SRV
 *
 *  =========================================================================
 */

/* 
 *  SDL Write Put and Service
 */
static int sdl_wput(queue_t * q, mblk_t * mp)
{
	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);
	}
	if (sdl_trylockq(q)) {
		switch (mp->b_datap->db_type) {
		case M_DATA:
			rtn = sdl_w_data(q, mp);
			break;
		case M_PROTO:
		case M_PCPROTO:
			rtn = sdl_w_proto(q, mp);
			break;
		case M_FLUSH:
			rtn = sdl_w_flush(q, mp);
			break;
		case M_IOCTL:
			rtn = sdl_w_ioctl(q, mp);
			break;
		default:
			rtn = sdl_m_other(q, mp);
			break;
		}
		switch (rtn) {
		case 0:
			assert(mp->b_datap->db_ref);
			freemsg(mp);
		case 1:
			break;
		case 2:
			assert(mp->b_datap->db_ref);
			freeb(mp);
			break;
		case -ENOBUFS:
			/* should set up bufcall */
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
			seldom();
			putq(q, mp);
			sdl_unlockq(q);
			return (0);
		default:
			rare();
			ptrace(("Error = %d\n", rtn));
			assert(mp->b_datap->db_ref);
			freemsg(mp);
			sdl_unlockq(q);
			return (rtn);
		}
	} else {
		rare();
		putq(q, mp);
	}
	return (0);
}

static int sdl_wsrv(queue_t * q)
{
	int rtn;
	mblk_t *mp;
	ensure(q, return (-EFAULT));
	if (sdl_trylockq(q)) {
		while ((mp = getq(q))) {
			switch (mp->b_datap->db_type) {
			case M_DATA:
				rtn = sdl_w_data(q, mp);
				break;
			case M_PROTO:
			case M_PCPROTO:
				rtn = sdl_w_proto(q, mp);
				break;
			case M_FLUSH:
				rtn = sdl_w_flush(q, mp);
				break;
			case M_IOCTL:
				rtn = sdl_w_ioctl(q, mp);
				break;
			default:
				rtn = sdl_m_other(q, mp);
				break;
			}
			switch (rtn) {
			case 0:
				assert(mp->b_datap->db_ref);
				freemsg(mp);
			case 1:
				continue;
			case 2:
				assert(mp->b_datap->db_ref);
				freeb(mp);
				continue;
			case -ENOBUFS:
				/* should set up bufcall */
			case -EBUSY:
			case -EAGAIN:
			case -ENOMEM:
				seldom();
				if (mp->b_datap->db_type < QPCTL) {
					putbq(q, mp);
					sdl_unlockq(q);
					return (0);
				}
				__ptrace(("This should never happen!\n"));
				if (mp->b_datap->db_type == M_PCPROTO) {
					mp->b_datap->db_type = M_PROTO;
					putq(q, mp);
					sdl_unlockq(q);
					return (0);
				}
			default:
				rare();
				ptrace(("Error = %d\n", rtn));
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				continue;
			}
		}
	} else {
		rare();
		qenable(q);
	}
	return (0);
}

/* 
 *  SCTP Read Put and Service
 */
static int sdl_rput(queue_t * q, mblk_t * mp)
{
	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);
	}
	if (sdl_trylockq(q)) {
		switch (mp->b_datap->db_type) {
		case M_DATA:
			rtn = sdl_r_data(q, mp);
			break;
		case M_PROTO:
		case M_PCPROTO:
			rtn = sdl_r_proto(q, mp);
			break;
			// case M_CTL: rtn = sdl_r_ctl (q, mp); break;
		case M_ERROR:
			rtn = sdl_r_error(q, mp);
			break;
		case M_FLUSH:
			rtn = sdl_r_flush(q, mp);
			break;
		default:
			rtn = sdl_m_other(q, mp);
			break;
		}
		switch (rtn) {
		case 0:
			assert(mp->b_datap->db_ref);
			freemsg(mp);
		case 1:
			break;
		case 2:
			assert(mp->b_datap->db_ref);
			freeb(mp);
			break;
		case -ENOBUFS:
			/* should set up bufcall */
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
			seldom();
			putq(q, mp);
			sdl_unlockq(q);
			return (0);
		default:
			rare();
			ptrace(("Error = %d\n", rtn));
			assert(mp->b_datap->db_ref);
			freemsg(mp);
			sdl_unlockq(q);
			return (rtn);
		}
	} else {
		rare();
		putq(q, mp);
	}
	return (0);
}

static int sdl_rsrv(queue_t * q)
{
	int rtn;
	mblk_t *mp;
	ensure(q, return (-EFAULT));
	if (sdl_trylockq(q)) {
		while ((mp = getq(q))) {
			switch (mp->b_datap->db_type) {
			case M_DATA:
				rtn = sdl_r_data(q, mp);
				break;
			case M_PROTO:
			case M_PCPROTO:
				rtn = sdl_r_proto(q, mp);
				break;
				// case M_CTL: rtn = sdl_r_ctl (q, mp); break;
			case M_ERROR:
				rtn = sdl_r_error(q, mp);
				break;
			case M_FLUSH:
				rtn = sdl_r_flush(q, mp);
				break;
			default:
				rtn = sdl_m_other(q, mp);
				break;
			}
			switch (rtn) {
			case 0:
				assert(mp->b_datap->db_ref);
				freemsg(mp);
			case 1:
				continue;
			case 2:
				assert(mp->b_datap->db_ref);
				freeb(mp);
				continue;
			case -ENOBUFS:
				/* should set up bufcall */
			case -EBUSY:
			case -EAGAIN:
			case -ENOMEM:
				seldom();
				if (mp->b_datap->db_type < QPCTL) {
					putbq(q, mp);
					sdl_unlockq(q);
					return (0);
				}
				__ptrace(("This should never happen!\n"));
				if (mp->b_datap->db_type == M_PCPROTO) {
					mp->b_datap->db_type = M_PROTO;
					putq(q, mp);
					sdl_unlockq(q);
					return (0);
				}
			default:
				rare();
				ptrace(("Error = %d\n", rtn));
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				continue;
			}
		}
	} else {
		rare();
		qenable(q);
	}
	return (0);
}

/*
 *  =========================================================================
 *
 *  Private Structure allocation, deallocation and cache
 *
 *  =========================================================================
 */
kmem_cache_t *sdl_cachep = NULL;

static void sdl_init_caches(void)
{
	if (!sdl_cachep &&
	    !(sdl_cachep = kmem_cache_create
	      ("sdl_cachep", sizeof(sdl_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL)))
		panic("%s:Cannot alloc sdl_cachep.\n", __FUNCTION__);
	return;
}
static void sdl_term_caches(void)
{
	if (sdl_cachep)
		if (kmem_cache_destroy(sdl_cachep))
			cmn_err(CE_WARN, "%s: did not destroy sdl_cachep", __FUNCTION__);
	return;
}

static sdl_t *sdl_alloc_priv(queue_t * q)
{
	sdl_t *sp;
	ensure(q, return (NULL));

	if ((sp = kmem_cache_alloc(sdl_cachep, SLAB_ATOMIC))) {
		bzero(sp, sizeof(*sp));
		RD(q)->q_ptr = WR(q)->q_ptr = sp;
		sp->rq = RD(q);
		sp->wq = WR(q);
		sp->state = LMI_DISABLED;
		lis_spin_lock_init(&sp->lock, "ss7sctp-private");
	}
	return (sp);
}
static void sdl_free_priv(queue_t * q)
{
	sdl_t *sp;
	ensure(q, return);
	sp = (sdl_t *) q->q_ptr;
	ensure(sp, return);
	untimeout(xchg(&sp->timer_tick, 0));
	freemsg(xchg(&sp->rx_buf, NULL));
	freemsg(xchg(&sp->tx_buf, NULL));
	kmem_cache_free(sdl_cachep, sp);
	return;
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 */
static int sdl_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	(void) crp;		/* for now */
	if (q->q_ptr != NULL)	/* already open */
		return (0);
	if (sflag == MODOPEN || WR(q)->q_next != NULL) {
//              fixme(("Check module we are being pushed over.\n"));
		if (!(sdl_alloc_priv(q)))
			return ENOMEM;
		return (0);
	}
	return EIO;
}
static int sdl_close(queue_t * q, int flag, cred_t * crp)
{
	(void) flag;
	(void) crp;

	sdl_free_priv(q);
	return (0);
}

/*
 *  =========================================================================
 *
 *  Lis Module Initialization
 *
 *  =========================================================================
 */
void sdl_init(void)
{
	int modnum;
	unless(sdl_minfo.mi_idnum, return);
	cmn_err(CE_NOTE, SDL_BANNER);	/* console splash */
	sdl_init_caches();
	if (!(modnum = lis_register_strmod(&sdl_info, sdl_minfo.mi_idname))) {
		sdl_minfo.mi_idnum = 0;
		rare();
		cmn_err(CE_NOTE, "sdl: couldn't register as module\n");
		return;
	}
	sdl_minfo.mi_idnum = modnum;
	return;
}
void sdl_terminate(void)
{
	ensure(sdl_minfo.mi_idnum, return);
	if ((sdl_minfo.mi_idnum = lis_unregister_strmod(&sdl_info)))
		cmn_err(CE_WARN, "sdl: couldn't unregister as module!\n");
	sdl_term_caches();
	return;
}

/*
 *  =========================================================================
 *
 *  Kernel Module Initialization
 *
 *  =========================================================================
 */
int init_module(void)
{
	sdl_init();
	return (0);
}
void cleanup_module(void)
{
	sdl_terminate();
	return;
}


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

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

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