/*****************************************************************************

 @(#) sdl_sctp.c,v SS7AlphaRelease(0.7.8.1) 2001/12/11 13:15:49

 -----------------------------------------------------------------------------

 Copyright (c) 2001  OpenSS7 Corporation <http://www.openss7.com>
 Copyright (c) 1997-2000  Brian F. G. Bidulock <bidulock@dallas.net>

 All Rights Reserved.

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 675 Mass
 Ave, Cambridge, MA 02139, USA.

 -----------------------------------------------------------------------------

 U.S. GOVERNMENT RESTRICTED RIGHTS.  If you are licensing this Software on
 behalf of the U.S. Government ("Government"), the following provisions apply
 to you.  If the Software is supplied by the Department of Defense ("DoD"), it
 is classified as "Commercial Computer Software" under paragraph 252.227-7014
 of the DoD Supplement to the Federal Acquisition Regulations ("DFARS") (or any
 successor regulations) and the Government is acquiring only the license rights
 granted herein (the license rights customarily provided to non-Government
 users).  If the Software is supplied to any unit or agency of the Government
 other than DoD, it is classified as "Restricted Computer Software" and the
 Government's rights in the Software are defined in paragraph 52.227-19 of the
 Federal Acquisition Regulations ("FAR") (or any success regulations) or, in
 the cases of NASA, in paragraph 18.52.227-86 of the NASA Supplement to the FAR
 (or any successor regulations).

 -----------------------------------------------------------------------------

 Last Modified 2001/12/11 13:15:49 by brian

 *****************************************************************************/

#ident "@(#) sdl_sctp.c,v SS7AlphaRelease(0.7.8.1) 2001/12/11 13:15:49"

static char const ident[] = "sdl_sctp.c,v SS7AlphaRelease(0.7.8.1) 2001/12/11 13:15:49";

#include <linux/config.h>
#include <linux/version.h>
#include <linux/modversions.h>
#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 "../debug.h"
#include "../bufq.h"

#define SDL_DESCRIP	"SS7/SCTP SIGNALLING DATA LINK (SDL) STREAMS MODULE."
#define SDL_COPYRIGHT	"Copyright (c) 2001 OpenSS7 Corp.  All Rights Reserved."
#define SDL_DEVICE	"Part of the OpenSS7 Stack for LiS STREAMS."
#define SDL_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#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);

#ifndef INT
#define INT void
#endif

//#define SDL_RX_COMPRESSION
//#define SDL_TX_COMPRESSION

typedef void (*bufcall_fnc_t)(long);

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */
static struct module_info sdl_minfo =
{
	0,		/* Module ID number		*/
	"sdl-sctp",	/* Module name			*/
	0,		/* Min packet size accepted	*/  /* FIXME */
	INFPSZ,		/* Max packet size accepted	*/  /* FIXME */
	1<<15,		/* Hi water mark		*/  /* FIXME */
	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 =
{
	sdl_rput,	/* Read put (msg from below)	*/
	sdl_rsrv,	/* Read queue service		*/
	sdl_open,	/* Each open			*/
	sdl_close,	/* Last close			*/
	NULL,		/* Admin (not used)		*/
	&sdl_minfo,	/* Information			*/
	NULL		/* Statistics			*/
};

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

static struct qinit sdl_winit =
{
	sdl_wput,	/* Write put (msg from above)	*/
	sdl_wsrv,	/* Write queue service		*/
	NULL,		/* Each open			*/
	NULL,		/* Last close			*/
	NULL,		/* Admin (not used)		*/
	&sdl_minfo,	/* Information			*/
	NULL		/* Statistics			*/
};

static struct streamtab sdl_info =
{
	&sdl_rinit,	/* Upper read  queue		*/
	&sdl_winit,	/* Upper write queue		*/
	NULL,		/* Lower read  queue		*/
	NULL		/* Lower write queue		*/
};

/*
 *  =========================================================================
 *
 *  SDL Private Structure
 *
 *  =========================================================================
 */
typedef struct sdl {
	struct sdl	*next;
	queue_t		*rq;
	queue_t		*wq;
	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_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);
}
/*
 *  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);
}
/*
 *  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
 *  ---------------------------------------------
 */
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);
}
/*
 *  LMI_STATS_IND
 *  ---------------------------------------------
 */
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 ( bcanputnext(sp->rq, mp->b_band) ) {
	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);
}
/*
 *  LMI_EVENT_IND
 *  ---------------------------------------------
 */
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 ( bcanputnext(sp->rq, mp->b_band) ) {
	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);
}
/*
 *  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
 *  ---------------------------------------------
 */
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);
}
/*
 *  SDL_DAEDR_SU_IN_ERROR_IND
 *  ---------------------------------------------
 */
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);
}
/*
 *  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;
	sdl_t *sp;
	ensure( q,  return((INT)(-EFAULT)) );
	sp = (sdl_t *)q->q_ptr;
	ensure( sp, 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_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);
			return(INT)(0);
		default:
			rare();
			ptrace(("Error = %d\n", rtn));
			assert(mp->b_datap->db_ref);
			freemsg(mp);
			return(INT)(rtn);
	}
	return(INT)(0);
}
static INT
sdl_wsrv(queue_t *q)
{
	int rtn;
	mblk_t *mp;
	sdl_t *sp;
	ensure( q,  return((INT)(-EFAULT)) );
	sp = (sdl_t *)q->q_ptr;
	ensure( sp, return((INT)(-EFAULT)) );
	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);
					return(INT)(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);
					return(INT)(0);
				}
			default:
				rare();
				ptrace(("Error = %d\n", rtn));
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				continue;
		}
	}
	return(INT)(0);
}

/*
 *  SCTP Read Put and Service
 */
static INT
sdl_rput(queue_t *q, mblk_t *mp)
{
	int rtn;
	sdl_t *sp;
	ensure( q,  return((INT)(-EFAULT)) );
	sp = (sdl_t *)q->q_ptr;
	ensure( sp, 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_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);
			return(INT)(0);
		default:
			rare();
			ptrace(("Error = %d\n", rtn));
			assert(mp->b_datap->db_ref);
			freemsg(mp);
			return(INT)(rtn);
	}
	return(INT)(0);
}
static INT
sdl_rsrv(queue_t *q)
{
	int rtn;
	mblk_t *mp;
	sdl_t *sp;
	ensure( q,  return((INT)(-EFAULT)) );
	sp = (sdl_t *)q->q_ptr;
	ensure( sp, return((INT)(-EFAULT)) );
	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);
					return(INT)(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);
					return(INT)(0);
				}
			default:
				rare();
				ptrace(("Error = %d\n", rtn));
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				continue;
		}
	}
	return(INT)(0);
}

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

static void
sdl_init_caches(void)
{
	if ( !sdl_cachep )
	if ( !(sdl_cachep = kmem_find_general_cachep(sizeof(sdl_t))) )
	if ( !(sdl_cachep = kmem_cache_create( "sdl_cachep", sizeof(sdl_t),
					0, SLAB_HWCACHE_ALIGN, NULL, NULL)) )
		panic(__FUNCTION__ ":Cannot alloc sdl_cachep.\n");
	return;
}
static void
sdl_term_caches(void)
{
	/*
	 *  We could try to shrink the caches byt that was a very bad idea
	 *  last time I tried (crashed kernel) so I won't do it here.
	 */
}

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;
	}
	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;
}
