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/sdli/sdl.c


File /code/strss7/drivers/sdli/sdl.c



#ident "@(#) $RCSfile: sdl.c,v $ $Name:  $($Revision: 0.8.2.8 $) $Date: 2003/04/03 19:50:57 $"

static char const ident[] = "$RCSfile: sdl.c,v $ $Name:  $($Revision: 0.8.2.8 $) $Date: 2003/04/03 19:50:57 $";

/*
 *  This is an SDL (Signalling Data Link) kernel module which provides the
 *  capabilities of the SDL to any pipe.  Its principal purpose is testing.
 */

#include <linux/config.h>
#include <linux/version.h>
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/cmn_err.h>

#include <ss7/lmi.h>
#include <ss7/lmi_ioctl.h>
#include <ss7/sdli.h>
#include <ss7/sdli_ioctl.h>

#include "../debug.h"
#include "../bufq.h"
#include "../priv.h"
#include "../lock.h"
#include "../queue.h"
#include "../allocb.h"
#include "../timer.h"

#define SDL_DESCRIP	"SS7/SDL: (Signalling Data Link) STREAMS MODULE."
#define SDL_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  All Rights Reserved."
#define SDL_DEVICES	"Supports STREAMS pipes."
#define SDL_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define SDL_BANNER	SDL_DESCRIP	"\n" \
			SDL_COPYRIGHT	"\n" \
			SDL_DEVICES	"\n" \
			SDL_CONTACT	"\n"
#define SDL_LICENSE	"GPL"

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

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

#define SDL_MOD_ID	SDL_IOC_MAGIC
#define SDL_MOD_NAME	"sdl"

STATIC struct module_info sdl_rinfo = {
	mi_idnum:SDL_MOD_ID,			/* Module ID number */
	mi_idname:SDL_MOD_NAME "-rd",		/* Module name */
	mi_minpsz:1,				/* Min packet size accepted */
	mi_maxpsz:128,				/* Max packet size accepted */
	mi_hiwat:1,				/* Hi water mark */
	mi_lowat:0,				/* Lo water mark */
};

STATIC struct module_info sdl_winfo = {
	mi_idnum:SDL_MOD_ID,			/* Module ID number */
	mi_idname:SDL_MOD_NAME "-wr",		/* Module name */
	mi_minpsz:1,				/* Min packet size accepted */
	mi_maxpsz:280,				/* Max packet size accepted */
	mi_hiwat:1,				/* Hi water mark */
	mi_lowat:0				/* Lo water mark */
};

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

STATIC struct qinit sdl_rinit = {
	qi_putp:ss7_oput,			/* Read put (message from below) */
	qi_qopen:sdl_open,			/* Each open */
	qi_qclose:sdl_close,			/* Last close */
	qi_minfo:&sdl_rinfo,			/* Information */
};

STATIC struct qinit sdl_winit = {
	qi_putp:ss7_iput,			/* Write put (message from above) */
	qi_minfo:&sdl_winfo,			/* Information */
};

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

/*
 *  ========================================================================
 *
 *  Private structure
 *
 *  ========================================================================
 */
typedef struct sdl {
	STR_DECLARATION (struct sdl);		/* stream declaration */
	unsigned long timestamp;		/* next tick timestamp */
	unsigned long bytecount;		/* bytes send within tick */
	unsigned long tickbytes;		/* bytes per tick */
	lmi_option_t option;			/* LMI protocol and variant options */
	sdl_timers_t timers;			/* SDL protocol timers */
	sdl_statem_t statem;			/* SDL state machine variables */
	sdl_config_t config;			/* SDL configuration options */
	sdl_notify_t notify;			/* SDL notification options */
	sdl_stats_t stats;			/* SDL statistics */
	sdl_stats_t stamp;			/* SDL statistics timestamps */
	lmi_sta_t statsp;			/* SDL statistics periods */
} sdl_t;
#define SDL_PRIV(__q) ((struct sdl *)(__q)->q_ptr)

struct sdl *sdl_opens = NULL;

STATIC struct sdl *sdl_alloc_priv(queue_t *, struct sdl **, dev_t *, cred_t *);
STATIC struct sdl *sdl_get(struct sdl *);
STATIC void sdl_put(struct sdl *);
STATIC void sdl_free_priv(queue_t *);

struct lmi_option lmi_default = {
	pvar:SS7_PVAR_ITUT_96,
	popt:0,
};

struct sdl_config sdl_default = {
	ifflags:0,
	iftype:SDL_TYPE_NONE,
	ifrate:64000,
	ifgtype:SDL_GTYPE_NONE,
	ifgrate:0,
	ifmode:SDL_MODE_PEER,
	ifgmode:SDL_MODE_NONE,
	ifgcrc:SDL_GCRC_NONE,
	ifclock:SDL_CLOCK_SHAPER,
	ifcoding:SDL_CODING_NONE,
	ifframing:SDL_FRAMING_NONE,
	ifleads:0,
	ifalarms:0,
	ifrxlevel:0,
	iftxlevel:0,
	ifsync:0,
	ifsyncsrc:{0, 0, 0, 0},
};

/*
 *  ========================================================================
 *
 *  PRIMITIVES
 *
 *  ========================================================================
 */
/*
 *  ------------------------------------------------------------------------
 *
 *  Primitives sent upstream
 *
 *  ------------------------------------------------------------------------
 */
/*
 *  M_ERROR
 *  -----------------------------------
 */
STATIC INLINE int m_error(queue_t *q, struct sdl *s, int err)
{
	mblk_t *mp;
	if ((mp = ss7_allocb(q, 2, BPRI_MED))) {
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_wptr)++ = err < 0 ? -err : err;
		*(mp->b_wptr)++ = err < 0 ? -err : err;
		s->i_state = LMI_UNUSABLE;
		printd(("%s: %p: <- M_ERROR\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  M_HANGUP
 *  -----------------------------------
 */
STATIC INLINE int m_hangup(queue_t *q, struct sdl *s, int err)
{
	mblk_t *mp;
	if ((mp = ss7_allocb(q, 2, BPRI_MED))) {
		mp->b_datap->db_type = M_HANGUP;
		*(mp->b_wptr)++ = err < 0 ? -err : err;
		*(mp->b_wptr)++ = err < 0 ? -err : err;
		s->i_state = LMI_UNUSABLE;
		printd(("%s: %p: <- M_HANGUP\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  LMI_INFO_ACK
 *  -----------------------------------
 */
STATIC INLINE int lmi_info_ack(queue_t *q, struct sdl *s)
{
	mblk_t *mp;
	lmi_info_ack_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_INFO_ACK;
		p->lmi_version = 1;
		p->lmi_state = s->i_state;
		p->lmi_max_sdu = 64;
		p->lmi_min_sdu = 8;
		p->lmi_header_len = 0;
		p->lmi_ppa_style = LMI_STYLE1;	/* only STYLE1 for modules */
		printd(("%s: %p: <- LMI_INFO_ACK\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  LMI_OK_ACK
 *  -----------------------------------
 */
STATIC INLINE int lmi_ok_ack(queue_t *q, struct sdl *s, long prim)
{
	mblk_t *mp;
	lmi_ok_ack_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_OK_ACK;
		p->lmi_correct_primitive = prim;
		switch (s->i_state) {
		case LMI_ATTACH_PENDING:
			s->i_state = LMI_DISABLED;
			break;
		case LMI_DETACH_PENDING:
			s->i_state = LMI_UNATTACHED;
			break;
		}
		p->lmi_state = s->i_state;
		printd(("%s: %p: <- LMI_OK_ACK\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  LMI_ERROR_ACK
 *  -----------------------------------
 */
STATIC INLINE int lmi_error_ack(queue_t *q, struct sdl *s, long prim, ulong reason, ulong errno)
{
	mblk_t *mp;
	lmi_error_ack_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_ERROR_ACK;
		p->lmi_errno = errno;
		p->lmi_reason = reason;
		p->lmi_error_primitive = prim;
		switch (s->i_state) {
		case LMI_ATTACH_PENDING:
			s->i_state = LMI_UNATTACHED;
			break;
		case LMI_DETACH_PENDING:
			s->i_state = LMI_DISABLED;
			break;
		case LMI_ENABLE_PENDING:
			s->i_state = LMI_DISABLED;
			break;
		case LMI_DISABLE_PENDING:
			s->i_state = LMI_ENABLED;
			break;
		default:
			break;
		}
		p->lmi_state = s->i_state;
		printd(("%s: %p: <- LMI_ERROR_ACK\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  LMI_ENABLE_CON
 *  -----------------------------------
 */
STATIC INLINE int lmi_enable_con(queue_t *q, struct sdl *s)
{
	mblk_t *mp;
	lmi_enable_con_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_ENABLE_CON;
		switch (s->i_state) {
		case LMI_ENABLE_PENDING:
			s->i_state = LMI_ENABLED;
			s->timestamp = jiffies;
			s->bytecount = 0;
			break;
		default:
			swerr();
			break;
		}
		p->lmi_state = s->i_state;
		printd(("%s: %p: <- LMI_ENABLE_CON\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  LMI_DISABLE_CON
 *  -----------------------------------
 */
STATIC INLINE int lmi_disable_con(queue_t *q, struct sdl *s)
{
	mblk_t *mp;
	lmi_disable_con_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_DISABLE_CON;
		switch (s->i_state) {
		case LMI_DISABLE_PENDING:
			s->i_state = LMI_DISABLED;
			break;
		default:
			swerr();
			break;
		}
		p->lmi_state = s->i_state;
		printd(("%s: %p: <- LMI_DISABLE_CON\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  LMI_OPTMGMT_ACK
 *  -----------------------------------
 */
STATIC INLINE int lmi_optmgmt_ack(queue_t *q, struct sdl *s, ulong flags, caddr_t opt_ptr, size_t opt_len)
{
	mblk_t *mp;
	lmi_optmgmt_ack_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p) + opt_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) 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;
		printd(("%s: %p: <- LMI_OPTMGMT_ACK\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  LMI_ERROR_IND
 *  -----------------------------------
 */
STATIC INLINE int lmi_error_ind(queue_t *q, struct sdl *s, long error, long reason)
{
	mblk_t *mp;
	lmi_error_ind_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_ERROR_IND;
		p->lmi_errno = error;
		p->lmi_reason = reason;
		p->lmi_state = s->i_state;
		printd(("%s: %p: <- LMI_ERROR_IND\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  LMI_STATS_IND
 *  -----------------------------------
 */
STATIC INLINE int lmi_stats_ind(queue_t *q, struct sdl *s)
{
	mblk_t *mp;
	lmi_stats_ind_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_STATS_IND;
		p->lmi_interval = 0;
		p->lmi_timestamp = jiffies;
		printd(("%s: %p: <- LMI_STATS_IND\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  LMI_EVENT_IND
 *  -----------------------------------
 */
STATIC INLINE int lmi_event_ind(queue_t *q, struct sdl *s, ulong oid, ulong level, caddr_t inf_ptr,
				size_t inf_len)
{
	mblk_t *mp;
	lmi_event_ind_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p) + inf_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_EVENT_IND;
		p->lmi_objectid = oid;
		p->lmi_timestamp = jiffies;
		p->lmi_severity = level;
		bcopy(inf_ptr, mp->b_wptr, inf_len);
		mp->b_wptr += inf_len;
		printd(("%s: %p: <- LMI_EVENT_IND\n", SDL_MOD_NAME, s));
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  SDL_RECEIVED_BITS_IND
 *  -----------------------------------
 */
STATIC INLINE int sdl_received_bits_ind(queue_t *q, struct sdl *s, mblk_t *dp)
{
	mblk_t *mp;
	sdl_received_bits_ind_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sdl_primitive = SDL_RECEIVED_BITS_IND;
		mp->b_cont = dp;
		putnext(s->oq, mp);
		return (QR_ABSORBED);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  SDL_DISCONNECT_IND
 *  -----------------------------------
 */
STATIC INLINE int sdl_disconnect_ind(queue_t *q, struct sdl *s)
{
	mblk_t *mp;
	sdl_disconnect_ind_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sdl_primitive = SDL_DISCONNECT_IND;
		putnext(s->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  =========================================================================
 *
 *  PROTOCOL STATE MACHINE FUNCTIONS
 *
 *  =========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  Timers
 *
 *  -------------------------------------------------------------------------
 */
enum { tall, t9 };

STATIC int sdl_t9_timeout(struct sdl *);

STATIC void sdl_t9_expiry(caddr_t data)
{
	ss7_do_timeout(data, "t9", "sdl", &((struct sdl *) data)->timers.t9,
		       (int (*)(struct head *)) &sdl_t9_timeout, &sdl_t9_expiry);
}

STATIC void sdl_stop_timer_t9(struct sdl *s)
{
	ss7_stop_timer((struct head *) s, "t9", "sdl", &s->timers.t9);
}

STATIC void sdl_start_timer_t9(struct sdl *s)
{
	ss7_start_timer((struct head *) s, "t9", "sdl", &s->timers.t9, &sdl_t9_expiry, s->timestamp - jiffies);
};

STATIC INLINE void __sdl_timer_stop(struct sdl *s, const uint t)
{
	int single = 1;
	switch (t) {
	case tall:
		single = 0;
		/* fall through */
	case t9:
		sdl_stop_timer_t9(s);
		if (single)
			break;
		/* fall through */
		break;
	default:
		swerr();
		break;
	}
}
STATIC INLINE void sdl_timer_stop(struct sdl *s, const uint t)
{
	int flags;
	lis_spin_lock_irqsave(&s->lock, &flags);
	{
		__sdl_timer_stop(s, t);
	}
	lis_spin_unlock_irqrestore(&s->lock, &flags);
}
STATIC INLINE void sdl_timer_start(struct sdl *s, const uint t)
{
	int flags;
	lis_spin_lock_irqsave(&s->lock, &flags);
	{
		__sdl_timer_stop(s, t);
		switch (t) {
		case t9:
			sdl_start_timer_t9(s);
			break;
		default:
			swerr();
			break;
		}
	}
	lis_spin_unlock_irqrestore(&s->lock, &flags);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  State Machine
 *
 *  -------------------------------------------------------------------------
 */

/*
 *  ========================================================================
 *
 *  EVENTS
 *
 *  ========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  Timer Events
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  T10 EXPIRY
 *  -----------------------------------
 */
STATIC int sdl_t9_timeout(struct sdl *s)
{
	/* wakeup the write queue */
	qenable(s->iq);
	return (QR_DONE);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  LM User -> LM Provider Primitives
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  LMI_INFO_REQ:
 *  -----------------------------------
 */
STATIC int lmi_info_req(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	lmi_info_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto emsgsize;
	return lmi_info_ack(q, s);
      emsgsize:
	return lmi_error_ack(q, s, LMI_INFO_REQ, LMI_PROTOSHORT, EMSGSIZE);
}

/*
 *  LMI_ATTACH_REQ:
 *  -----------------------------------
 */
STATIC int lmi_attach_req(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	lmi_attach_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto emsgsize;
	if (s->i_state == LMI_UNUSABLE)
		goto eagain;
	if (s->i_style != LMI_STYLE2)
		goto eopnotsupp;
	if (s->i_state != LMI_UNATTACHED)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p) + 2)
		goto badppa;
	s->i_state = LMI_ATTACH_PENDING;
	return lmi_ok_ack(q, s, LMI_ATTACH_REQ);
      badppa:
	ptrace(("%s: %p: PROTO: bad ppa (too short)\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_ATTACH_REQ, LMI_BADPPA, EMSGSIZE);
      outstate:
	ptrace(("%s: %p: PROTO: out of state\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_ATTACH_REQ, LMI_OUTSTATE, EPROTO);
      eopnotsupp:
	ptrace(("%s: %p: PROTO: primitive not supported for style\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_ATTACH_REQ, LMI_NOTSUPP, EOPNOTSUPP);
      eagain:
	ptrace(("%s: %p: INFO: waiting for streams to become usable\n", SDL_MOD_NAME, s));
	return (-EAGAIN);
      emsgsize:
	ptrace(("%s: %p: PROTO: M_PROTO block too short\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_ATTACH_REQ, LMI_PROTOSHORT, EMSGSIZE);
}

/*
 *  LMI_DETACH_REQ:
 *  -----------------------------------
 */
STATIC int lmi_detach_req(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	lmi_detach_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto emsgsize;
	if (s->i_state == LMI_UNUSABLE)
		goto eagain;
	if (s->i_style != LMI_STYLE2)
		goto eopnotsupp;
	if (s->i_state != LMI_DISABLED)
		goto outstate;
	s->i_state = LMI_DETACH_PENDING;
	return lmi_ok_ack(q, s, LMI_DETACH_REQ);
      outstate:
	ptrace(("%s: %p: PROTO: out of state\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_DETACH_REQ, LMI_OUTSTATE, EPROTO);
      eopnotsupp:
	ptrace(("%s: %p: PROTO: primitive not supported for style\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_DETACH_REQ, LMI_NOTSUPP, EOPNOTSUPP);
      eagain:
	ptrace(("%s: %p: INFO: waiting for streams to become usable\n", SDL_MOD_NAME, s));
	return (-EAGAIN);
      emsgsize:
	ptrace(("%s: %p: PROTO: M_PROTO block too short\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_DETACH_REQ, LMI_PROTOSHORT, EMSGSIZE);
}

/*
 *  LMI_ENABLE_REQ:
 *  -----------------------------------
 */
STATIC int lmi_enable_req(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	lmi_enable_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto emsgsize;
	if (s->i_state == LMI_UNUSABLE)
		goto eagain;
	if (s->i_state != LMI_DISABLED)
		goto outstate;
	s->i_state = LMI_ENABLE_PENDING;
	return lmi_enable_con(q, s);
      outstate:
	ptrace(("%s: %p: PROTO: out of state\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_ENABLE_REQ, LMI_OUTSTATE, EPROTO);
      eagain:
	ptrace(("%s: %p: INFO: waiting for streams to become usable\n", SDL_MOD_NAME, s));
	return (-EAGAIN);
      emsgsize:
	ptrace(("%s: %p: PROTO: M_PROTO block too short\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_ENABLE_REQ, LMI_PROTOSHORT, EMSGSIZE);
}

/*
 *  LMI_DISABLE_REQ:
 *  -----------------------------------
 */
STATIC int lmi_disable_req(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	lmi_disable_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto emsgsize;
	if (s->i_state == LMI_UNUSABLE)
		goto eagain;
	if (s->i_state != LMI_ENABLED)
		goto outstate;
	s->i_state = LMI_DISABLE_PENDING;
	return lmi_disable_con(q, s);
      outstate:
	ptrace(("%s: %p: PROTO: out of state\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_DISABLE_REQ, LMI_OUTSTATE, EPROTO);
      eagain:
	ptrace(("%s: %p: INFO: waiting for streams to become usable\n", SDL_MOD_NAME, s));
	return (-EAGAIN);
      emsgsize:
	ptrace(("%s: %p: PROTO: M_PROTO block too short\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_DISABLE_REQ, LMI_PROTOSHORT, EMSGSIZE);
}

/*
 *  LMI_OPTMGMT_REQ:
 *  -----------------------------------
 */
STATIC int lmi_optmgmt_req(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	lmi_optmgmt_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto emsgsize;
	goto eopnotsupp;
      eopnotsupp:
	ptrace(("%s: %p: PROTO: primitive not supported\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_OPTMGMT_REQ, LMI_NOTSUPP, EOPNOTSUPP);
      emsgsize:
	(void) s;
	ptrace(("%s: %p: PROTO: M_PROTO block too short\n", SDL_MOD_NAME, s));
	return lmi_error_ack(q, s, LMI_OPTMGMT_REQ, LMI_PROTOSHORT, EMSGSIZE);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  SDL User -> SDL Provider Primitives
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  M_DATA
 *  -----------------------------------
 */
STATIC int sdl_send_data(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	int size;
	if (s->i_state != LMI_ENABLED)
		goto outstate;
	if (s->timestamp > jiffies) {
		printd(("%s: %p: %s sleeping for %ld ms\n", SDL_MOD_NAME, s, __FUNCTION__,
			s->timestamp - jiffies));
		sdl_timer_start(s, t9);
		return (-EAGAIN);
	}
	if (s->timestamp < jiffies) {
		s->bytecount = 0;
		s->timestamp = jiffies;
		s->stats.tx_underruns++;
	}
	if (s->statem.tx_state != SDL_STATE_IDLE) {
		size = msgdsize(mp);
	} else {
		/* idle when transmitter shut down */
		mblk_t *dp;
		for (size = 0, dp = mp; dp; dp = dp->b_cont) {
			if (dp->b_wptr > dp->b_rptr) {
				int len = dp->b_wptr - dp->b_rptr;
				size += len;
				memset(dp->b_rptr, 0xff, len);
			}
		}
	}
	putnext(s->iq, mp);
	s->stats.tx_octets += size;
	s->bytecount += size;
	ensure(s->tickbytes > 0, s->tickbytes = 1);
	while (s->bytecount >= s->tickbytes) {
		s->bytecount -= s->tickbytes;
		s->timestamp++;
	}
	return (QR_ABSORBED);
      outstate:
	return (-EPROTO);	/* ignore */
}

/*
 *  SDL_BITS_FOR_TRANSMISSION_REQ
 *  -----------------------------------
 *  This is the non-preferred way of sending data.  It is preferred that
 *  M_DATA blocks are used (above).  We just strip off the M_PROTO and requeue
 *  only the M_DATA blocks.
 */
STATIC int sdl_bits_for_transmission_req(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	sdl_bits_for_transmission_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto emsgsize;
	if (s->i_state == LMI_UNUSABLE)
		goto eagain;
	if (s->i_state != LMI_ENABLED)
		goto outstate;
	if (!mp->b_cont || mp->b_cont->b_datap->db_type != M_DATA)
		goto eproto;
	trace();
	return (QR_STRIP);
      eproto:
	ptrace(("%s: %p: ERROR: bad message\n", SDL_MOD_NAME, s));
	return m_error(q, s, EPROTO);
      outstate:
	ptrace(("%s: %p: PROTO: out of state\n", SDL_MOD_NAME, s));
	return m_error(q, s, EPROTO);
      eagain:
	ptrace(("%s: %p: INFO: waiting for streams to become usable\n", SDL_MOD_NAME, s));
	return (-EAGAIN);
      emsgsize:
	ptrace(("%s: %p: PROTO: M_PROTO block too short\n", SDL_MOD_NAME, s));
	return m_error(q, s, EPROTO);
}

/*
 *  SDL_CONNECT_REQ:
 *  -----------------------------------
 */
STATIC int sdl_connect_req(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	sdl_connect_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto emsgsize;
	if (s->i_state == LMI_UNUSABLE)
		goto eagain;
	if (s->i_state != LMI_ENABLED)
		goto outstate;
	s->statem.tx_state = SDL_STATE_IN_SERVICE;
	s->statem.rx_state = SDL_STATE_IN_SERVICE;
	return (QR_DONE);
      outstate:
	ptrace(("%s: %p: PROTO: out of state\n", SDL_MOD_NAME, s));
	return m_error(q, s, EPROTO);
      eagain:
	ptrace(("%s: %p: INFO: waiting for streams to become usable\n", SDL_MOD_NAME, s));
	return (-EAGAIN);
      emsgsize:
	ptrace(("%s: %p: PROTO: M_PROTO block too short\n", SDL_MOD_NAME, s));
	return m_error(q, s, EPROTO);
}

/*
 *  SDL_DISCONNECT_REQ:
 *  -----------------------------------
 */
STATIC int sdl_disconnect_req(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	sdl_connect_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto emsgsize;
	if (s->i_state == LMI_UNUSABLE)
		goto eagain;
	if (s->i_state != LMI_ENABLED)
		goto outstate;
	s->statem.tx_state = SDL_STATE_IDLE;
	s->statem.rx_state = SDL_STATE_IDLE;
	return (QR_DONE);
      outstate:
	ptrace(("%s: %p: PROTO: out of state\n", SDL_MOD_NAME, s));
	return m_error(q, s, EPROTO);
      eagain:
	ptrace(("%s: %p: INFO: waiting for streams to become usable\n", SDL_MOD_NAME, s));
	return (-EAGAIN);
      emsgsize:
	ptrace(("%s: %p: PROTO: M_PROTO block too short\n", SDL_MOD_NAME, s));
	return m_error(q, s, EPROTO);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  SDL-Provider -> SDL-User Primitives
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  M_DATA
 *  -----------------------------------
 */
STATIC int sdl_recv_data(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	int size;
	if (s->i_state != LMI_ENABLED)
		goto outstate;
	if (s->statem.rx_state != SDL_STATE_IDLE) {
		size = msgdsize(mp);
	} else {
		/* idle when receiver shut down */
		mblk_t *dp;
		for (size = 0, dp = mp; dp; dp = dp->b_cont) {
			if (dp->b_wptr > dp->b_rptr) {
				int len = dp->b_wptr - dp->b_rptr;
				size += len;
				memset(dp->b_rptr, 0xff, len);
			}
		}
	}
	s->stats.rx_octets += size;
	putnext(s->oq, mp);
	return (QR_ABSORBED);
      outstate:
	return (QR_DONE);	/* ignore */
}

/* 
 *  =========================================================================
 *
 *  IO Controls
 *
 *  =========================================================================
 *
 *  LM IO Controls
 *
 *  -------------------------------------------------------------------------
 */
STATIC int lmi_test_config(struct sdl *sdl, lmi_config_t * arg)
{
	return (-EOPNOTSUPP);
}
STATIC int lmi_commit_config(struct sdl *sdl, lmi_config_t * arg)
{
	return (-EOPNOTSUPP);
}
STATIC int lmi_iocgoptions(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags, ret = 0;
		lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			*arg = s->option;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_iocsoptions(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags, ret = 0;
		lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			s->option = *arg;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_iocgconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		lmi_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			arg->version = s->i_version;
			arg->style = s->i_style;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_iocsconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		lmi_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			s->i_version = arg->version;
			s->i_style = arg->style;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_ioctconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		lmi_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		return lmi_test_config(s, arg);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_ioccconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		lmi_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		return lmi_commit_config(s, arg);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_iocgstatem(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		lmi_statem_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			arg->state = s->i_state;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_ioccmreset(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		lmi_statem_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		int flags, ret = 0;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			s->i_state = LMI_UNUSABLE;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_iocgstatsp(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		lmi_sta_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		int flags, ret = 0;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			*arg = s->statsp;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_iocsstatsp(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		lmi_sta_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		int flags, ret = 0;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			s->statsp = *arg;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_iocgstats(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		lmi_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		int flags, ret;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			ret = -EOPNOTSUPP;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_ioccstats(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	int flags, ret;
	(void) mp;
	lis_spin_lock_irqsave(&s->lock, &flags);
	{
		ret = -EOPNOTSUPP;
	}
	lis_spin_unlock_irqrestore(&s->lock, &flags);
	return (ret);
}
STATIC int lmi_iocgnotify(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		lmi_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		int flags, ret;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			ret = -EOPNOTSUPP;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_iocsnotify(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		lmi_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		int flags, ret;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			ret = -EOPNOTSUPP;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}
STATIC int lmi_ioccnotify(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		lmi_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		int flags, ret;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			ret = -EOPNOTSUPP;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  SDL IO Controls
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sdl_test_config(struct sdl *s, sdl_config_t * arg)
{
	(void) s;
	(void) arg;
	// fixme(("%s: FIXME: write this function\n", SDL_MOD_NAME));
	return (0);
}
STATIC void sdl_commit_config(struct sdl *s, sdl_config_t * arg)
{
	s->config = *arg;
	/* reshape traffic */
	if (s->timestamp > jiffies) {
		sdl_timer_stop(s, t9);
		s->bytecount += s->tickbytes * (s->timestamp - jiffies);
	}
	if (s->timestamp < jiffies) {
		s->bytecount = 0;
		s->stats.tx_underruns++;
	}
	s->timestamp = jiffies;
	s->tickbytes = s->config.ifrate / HZ / 8;
	ensure(s->tickbytes > 0, s->tickbytes = 1);
	while (s->bytecount >= s->tickbytes) {
		s->bytecount -= s->tickbytes;
		s->timestamp++;
	}
	if (s->timestamp > jiffies) {
		printd(("%s: %p: %s sleeping for %ld ms\n", SDL_MOD_NAME, s, __FUNCTION__,
			s->timestamp - jiffies));
		sdl_timer_start(s, t9);
	}
}
STATIC int sdl_iocgoptions(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			*arg = s->option;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_iocsoptions(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			s->option = *arg;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_iocgconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			*arg = s->config;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_iocsconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int ret, flags = 0;
		sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			if (!(ret = sdl_test_config(s, arg)))
				sdl_commit_config(s, arg);
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_ioctconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		int ret, flags = 0;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			ret = sdl_test_config(s, arg);
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (ret);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_ioccconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		int flags = 0;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			sdl_commit_config(s, arg);
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_iocgstatem(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		sdl_statem_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			*arg = s->statem;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_ioccmreset(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	(void) s;
	(void) mp;
	// fixme(("%s: FIXME: Support master reset\n", SDL_MOD_NAME));
	return (-EOPNOTSUPP);
}
STATIC int sdl_iocgstatsp(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		lmi_sta_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			*arg = s->statsp;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_iocsstatsp(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		lmi_sta_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			s->statsp = *arg;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_iocgstats(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		sdl_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			*arg = s->stats;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_ioccstats(queue_t *q, mblk_t *mp)
{
	int flags = 0;
	struct sdl *s = SDL_PRIV(q);
	(void) mp;
	lis_spin_lock_irqsave(&s->lock, &flags);
	{
		bzero(&s->stats, sizeof(s->stats));
	}
	lis_spin_unlock_irqrestore(&s->lock, &flags);
	return (0);
}
STATIC int sdl_iocgnotify(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		sdl_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			*arg = s->notify;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_iocsnotify(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		sdl_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			s->notify = *arg;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_ioccnotify(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct sdl *s = SDL_PRIV(q);
		int flags = 0;
		sdl_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&s->lock, &flags);
		{
			s->notify.events &= ~arg->events;
		}
		lis_spin_unlock_irqrestore(&s->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int sdl_ioccdisctx(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	int flags = 0;
	lis_spin_lock_irqsave(&s->lock, &flags);
	{
		s->config.ifflags &= ~SDL_IF_TX_RUNNING;
		s->statem.tx_state = SDL_STATE_IDLE;
	}
	lis_spin_unlock_irqrestore(&s->lock, &flags);
	return (0);
}
STATIC int sdl_ioccconntx(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	int flags = 0;
	lis_spin_lock_irqsave(&s->lock, &flags);
	{
		s->config.ifflags |= SDL_IF_TX_RUNNING;
		s->statem.tx_state = SDL_STATE_IN_SERVICE;
	}
	lis_spin_unlock_irqrestore(&s->lock, &flags);
	return (0);
}

/*
 *  ========================================================================
 *
 *  STREAMS Message Handling
 *
 *  ========================================================================
 *
 *  M_IOCTL Handling
 *  -----------------------------------------------------------------------
 */
STATIC int sdl_w_ioctl(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	struct iocblk *iocp = (struct iocblk *) mp->b_rptr;
	void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL;
	int cmd = iocp->ioc_cmd, count = iocp->ioc_count;
	int type = _IOC_TYPE(cmd), nr = _IOC_NR(cmd), size = _IOC_SIZE(cmd);
	struct linkblk *lp = (struct linkblk *) arg;
	int ret = 0;
	switch (type) {
	case __SID:
	{
		switch (nr) {
		case _IOC_NR(I_STR):
		case _IOC_NR(I_LINK):
		case _IOC_NR(I_PLINK):
		case _IOC_NR(I_UNLINK):
		case _IOC_NR(I_PUNLINK):
			(void) lp;
			ptrace(("%s: %p: ERROR: Unsupported STREAMS ioctl %d\n", SDL_MOD_NAME, s, nr));
			ret = (-EINVAL);
			break;
		default:
			ptrace(("%s: %p: ERROR: Unsupported STREAMS ioctl %d\n", SDL_MOD_NAME, s, nr));
			ret = (-EOPNOTSUPP);
			break;
		}
		break;
	}
	case LMI_IOC_MAGIC:
	{
		if (count < size || s->i_state == LMI_UNATTACHED) {
			ret = (-EINVAL);
			break;
		}
		switch (nr) {
		case _IOC_NR(LMI_IOCGOPTIONS):	/* lmi_option_t */
			ret = lmi_iocgoptions(q, mp);
			break;
		case _IOC_NR(LMI_IOCSOPTIONS):	/* lmi_option_t */
			ret = lmi_iocsoptions(q, mp);
			break;
		case _IOC_NR(LMI_IOCGCONFIG):	/* lmi_config_t */
			ret = lmi_iocgconfig(q, mp);
			break;
		case _IOC_NR(LMI_IOCSCONFIG):	/* lmi_config_t */
			ret = lmi_iocsconfig(q, mp);
			break;
		case _IOC_NR(LMI_IOCTCONFIG):	/* lmi_config_t */
			ret = lmi_ioctconfig(q, mp);
			break;
		case _IOC_NR(LMI_IOCCCONFIG):	/* lmi_config_t */
			ret = lmi_ioccconfig(q, mp);
			break;
		case _IOC_NR(LMI_IOCGSTATEM):	/* lmi_statem_t */
			ret = lmi_iocgstatem(q, mp);
			break;
		case _IOC_NR(LMI_IOCCMRESET):	/* lmi_statem_t */
			ret = lmi_ioccmreset(q, mp);
			break;
		case _IOC_NR(LMI_IOCGSTATSP):	/* lmi_sta_t */
			ret = lmi_iocgstatsp(q, mp);
			break;
		case _IOC_NR(LMI_IOCSSTATSP):	/* lmi_sta_t */
			ret = lmi_iocsstatsp(q, mp);
			break;
		case _IOC_NR(LMI_IOCGSTATS):	/* lmi_stats_t */
			ret = lmi_iocgstats(q, mp);
			break;
		case _IOC_NR(LMI_IOCCSTATS):	/* lmi_stats_t */
			ret = lmi_ioccstats(q, mp);
			break;
		case _IOC_NR(LMI_IOCGNOTIFY):	/* lmi_notify_t */
			ret = lmi_iocgnotify(q, mp);
			break;
		case _IOC_NR(LMI_IOCSNOTIFY):	/* lmi_notify_t */
			ret = lmi_iocsnotify(q, mp);
			break;
		case _IOC_NR(LMI_IOCCNOTIFY):	/* lmi_notify_t */
			ret = lmi_ioccnotify(q, mp);
			break;
		default:
			ptrace(("%s: %p: ERROR: Unsupported LMI ioctl %d\n", SDL_MOD_NAME, s, nr));
			ret = -EOPNOTSUPP;
			break;
		}
		break;
	}
	case SDL_IOC_MAGIC:
	{
		if (count < size || s->i_state == LMI_UNATTACHED) {
			ret = (-EINVAL);
			break;
		}
		switch (nr) {
		case _IOC_NR(SDL_IOCGOPTIONS):	/* lmi_option_t */
			ret = sdl_iocgoptions(q, mp);
			break;
		case _IOC_NR(SDL_IOCSOPTIONS):	/* lmi_option_t */
			ret = sdl_iocsoptions(q, mp);
			break;
		case _IOC_NR(SDL_IOCGCONFIG):	/* sdl_config_t */
			ret = sdl_iocgconfig(q, mp);
			break;
		case _IOC_NR(SDL_IOCSCONFIG):	/* sdl_config_t */
			ret = sdl_iocsconfig(q, mp);
			break;
		case _IOC_NR(SDL_IOCTCONFIG):	/* sdl_config_t */
			ret = sdl_ioctconfig(q, mp);
			break;
		case _IOC_NR(SDL_IOCCCONFIG):	/* sdl_config_t */
			ret = sdl_ioccconfig(q, mp);
			break;
		case _IOC_NR(SDL_IOCGSTATEM):	/* sdl_statem_t */
			ret = sdl_iocgstatem(q, mp);
			break;
		case _IOC_NR(SDL_IOCCMRESET):	/* sdl_statem_t */
			ret = sdl_ioccmreset(q, mp);
			break;
		case _IOC_NR(SDL_IOCGSTATSP):	/* lmi_sta_t */
			ret = sdl_iocgstatsp(q, mp);
			break;
		case _IOC_NR(SDL_IOCSSTATSP):	/* lmi_sta_t */
			ret = sdl_iocsstatsp(q, mp);
			break;
		case _IOC_NR(SDL_IOCGSTATS):	/* sdl_stats_t */
			ret = sdl_iocgstats(q, mp);
			break;
		case _IOC_NR(SDL_IOCCSTATS):	/* sdl_stats_t */
			ret = sdl_ioccstats(q, mp);
			break;
		case _IOC_NR(SDL_IOCGNOTIFY):	/* sdl_notify_t */
			ret = sdl_iocgnotify(q, mp);
			break;
		case _IOC_NR(SDL_IOCSNOTIFY):	/* sdl_notify_t */
			ret = sdl_iocsnotify(q, mp);
			break;
		case _IOC_NR(SDL_IOCCNOTIFY):	/* sdl_notify_t */
			ret = sdl_ioccnotify(q, mp);
			break;
		case _IOC_NR(SDL_IOCCDISCTX):	/* */
			ret = sdl_ioccdisctx(q, mp);
			break;
		case _IOC_NR(SDL_IOCCCONNTX):	/* */
			ret = sdl_ioccconntx(q, mp);
			break;
		default:
			ptrace(("%s: ERROR: Unsupported SDL ioctl %d\n", SDL_MOD_NAME, nr));
			ret = -EOPNOTSUPP;
			break;
		}
		break;
	}
	default:
		ret = (QR_PASSFLOW);
		break;
	}
	if (ret > 0) {
		return (ret);
	} else if (ret == 0) {
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval = 0;
	} else {
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = -ret;
		iocp->ioc_rval = -1;
	}
	qreply(q, mp);
	return (QR_ABSORBED);
}

/*
 *  -----------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -----------------------------------------------------------------------
 */
/*
 *  Primitives from User to SDL.
 *  -----------------------------------
 */
STATIC int sdl_w_proto(queue_t *q, mblk_t *mp)
{
	int rtn;
	ulong prim;
	struct sdl *s = SDL_PRIV(q);
	ulong oldstate = s->i_state;
	if ((prim = *(ulong *) mp->b_rptr) == SDL_BITS_FOR_TRANSMISSION_REQ) {
		printd(("%s: %p: -> SDL_BITS_FOR_TRANSMISSION_REQ [%d]\n", SDL_MOD_NAME, s,
			msgdsize(mp->b_cont)));
		if ((rtn = sdl_bits_for_transmission_req(q, mp)) < 0)
			s->i_state = oldstate;
		return (rtn);
	}
	switch (prim) {
	case SDL_BITS_FOR_TRANSMISSION_REQ:
		printd(("%s: %p: -> SDL_BITS_FOR_TRANSMISSION_REQ [%d]\n", SDL_MOD_NAME, s,
			msgdsize(mp->b_cont)));
		rtn = sdl_bits_for_transmission_req(q, mp);
		break;
	case SDL_CONNECT_REQ:
		printd(("%s: %p: -> SDL_CONNECT_REQ\n", SDL_MOD_NAME, s));
		rtn = sdl_connect_req(q, mp);
		break;
	case SDL_DISCONNECT_REQ:
		printd(("%s: %p: -> SDL_DISCONNECT_REQ\n", SDL_MOD_NAME, s));
		rtn = sdl_disconnect_req(q, mp);
		break;
	case LMI_INFO_REQ:
		printd(("%s: %p: -> LMI_INFO_REQ\n", SDL_MOD_NAME, s));
		rtn = lmi_info_req(q, mp);
		break;
	case LMI_ATTACH_REQ:
		printd(("%s: %p: -> LMI_ATTACH_REQ\n", SDL_MOD_NAME, s));
		rtn = lmi_attach_req(q, mp);
		break;
	case LMI_DETACH_REQ:
		printd(("%s: %p: -> LMI_DETACH_REQ\n", SDL_MOD_NAME, s));
		rtn = lmi_detach_req(q, mp);
		break;
	case LMI_ENABLE_REQ:
		printd(("%s: %p: -> LMI_ENABLE_REQ\n", SDL_MOD_NAME, s));
		rtn = lmi_enable_req(q, mp);
		break;
	case LMI_DISABLE_REQ:
		printd(("%s: %p: -> LMI_DISABLE_REQ\n", SDL_MOD_NAME, s));
		rtn = lmi_disable_req(q, mp);
		break;
	case LMI_OPTMGMT_REQ:
		printd(("%s: %p: -> LMI_OPTMGMT_REQ\n", SDL_MOD_NAME, s));
		rtn = lmi_optmgmt_req(q, mp);
		break;
	default:
		/* discard anything we don't recognize */
		swerr();
		rtn = (-EOPNOTSUPP);
		break;
	}
	if (rtn < 0)
		s->i_state = oldstate;
	return (rtn);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int sdl_w_data(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	(void) s;
	printd(("%s: %p: -> M_DATA [%d]\n", SDL_MOD_NAME, s, msgdsize(mp)));
	return sdl_send_data(q, mp);
}
STATIC INLINE int sdl_r_data(queue_t *q, mblk_t *mp)
{
	struct sdl *s = SDL_PRIV(q);
	(void) s;
	printd(("%s: %p: M_DATA [%d] <-\n", SDL_MOD_NAME, s, msgdsize(mp)));
	return sdl_recv_data(q, mp);
}

/*
 *  =========================================================================
 *
 *  PUT and SRV
 *
 *  =========================================================================
 */
STATIC INLINE int sdl_w_prim(queue_t *q, mblk_t *mp)
{
	/* Fast Path */
	if (mp->b_datap->db_type == M_DATA)
		return sdl_w_data(q, mp);
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sdl_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return sdl_w_proto(q, mp);
	case M_FLUSH:
		return ss7_w_flush(q, mp);
	case M_IOCTL:
		return sdl_w_ioctl(q, mp);
	}
	return (QR_PASSALONG);
}
STATIC INLINE int sdl_r_prim(queue_t *q, mblk_t *mp)
{
	/* Fast Path */
	if (mp->b_datap->db_type == M_DATA)
		return sdl_r_data(q, mp);
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sdl_r_data(q, mp);
	case M_FLUSH:
		return ss7_r_flush(q, mp);
	}
	return (QR_PASSALONG);
}

/*
 *  =======================================================================
 *
 *  OPEN and CLOSE
 *
 *  =======================================================================
 */
STATIC int sdl_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
	MOD_INC_USE_COUNT;	/* keep module from unloading in our face */
	if (q->q_ptr != NULL) {
		MOD_DEC_USE_COUNT;
		return (0);	/* already open */
	}
	if (sflag == MODOPEN || WR(q)->q_next != NULL) {
		int cmajor = getmajor(*devp);
		int cminor = getminor(*devp);
		struct sdl *s;
		/* test for multiple push */
		for (s = sdl_opens; s; s = s->next) {
			if (s->u.dev.cmajor == cmajor && s->u.dev.cminor == cminor) {
				MOD_DEC_USE_COUNT;
				return (ENXIO);
			}
		}
		if (!(sdl_alloc_priv(q, &sdl_opens, devp, crp))) {
			MOD_DEC_USE_COUNT;
			return (ENOMEM);
		}
		return (0);
	}
	MOD_DEC_USE_COUNT;
	return (EIO);
}
STATIC int sdl_close(queue_t *q, int flag, cred_t *crp)
{
	(void) flag;
	(void) crp;
	sdl_free_priv(q);
	MOD_DEC_USE_COUNT;
	return (0);
}

/*
 *  ========================================================================
 *
 *  PRIVATE struct allocation, deallocation and cache
 *
 *  ========================================================================
 */
STATIC kmem_cache_t *sdl_priv_cachep = NULL;
STATIC int sdl_init_caches(void)
{
	if (!sdl_priv_cachep
	    && !(sdl_priv_cachep =
		 kmem_cache_create("sdl_priv_cachep", sizeof(struct sdl), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) {
		cmn_err(CE_PANIC, "%s: Cannot allocate sdl_priv_cachep", __FUNCTION__);
		return (-ENOMEM);
	}
	printd(("%s: Allocated/selected private structure cache\n", SDL_MOD_NAME));
	return (0);
}
STATIC void sdl_free_caches(void)
{
	if (sdl_priv_cachep) {
		if (kmem_cache_destroy(sdl_priv_cachep))
			cmn_err(CE_WARN, "%s: id not destroy sdl_priv_cachep.", __FUNCTION__);
		else
			printd(("%s: destroyed sdl_priv_cachep\n", SDL_MOD_NAME));
	}
	return;
}
STATIC struct sdl *sdl_alloc_priv(queue_t *q, struct sdl **sp, dev_t *devp, cred_t *crp)
{
	struct sdl *s;
	if ((s = kmem_cache_alloc(sdl_priv_cachep, SLAB_ATOMIC))) {
		printd(("%s: %p: allocated module private structure\n", SDL_MOD_NAME, s));
		bzero(s, sizeof(*s));
		sdl_get(s);	/* first get */
		s->u.dev.cmajor = getmajor(*devp);
		s->u.dev.cminor = getminor(*devp);
		s->cred = *crp;
		(s->oq = RD(q))->q_ptr = sdl_get(s);
		(s->iq = WR(q))->q_ptr = sdl_get(s);
		lis_spin_lock_init(&s->qlock, "sdl-queue-lock");
		s->o_prim = &sdl_r_prim;
		s->i_prim = &sdl_w_prim;
		s->o_wakeup = NULL;	// &sdl_rx_wakeup;
		s->i_wakeup = NULL;	// &sdl_tx_wakeup;
		s->i_state = LMI_DISABLED;
		s->i_style = LMI_STYLE1;
		s->i_version = 1;
		lis_spin_lock_init(&s->qlock, "sdl-priv_lock");
		if ((s->next = *sp))
			s->next->prev = &s->next;
		s->prev = sp;
		*sp = sdl_get(s);
		printd(("%s: %p: linked module private structure\n", SDL_MOD_NAME, s));
		s->timestamp = jiffies;
		s->tickbytes = s->config.ifrate / HZ / 8;
		s->bytecount = 0;
		/* configuration defaults */
		s->option = lmi_default;
		s->config = sdl_default;
		printd(("%s: %p: setting module private structure defaults\n", SDL_MOD_NAME, s));
	} else
		ptrace(("%s: ERROR: Could not allocate module private structure\n", SDL_MOD_NAME));
	return (s);
}
STATIC void sdl_free_priv(queue_t *q)
{
	struct sdl *s = SDL_PRIV(q);
	int flags = 0;
	ensure(s, return);
	lis_spin_lock_irqsave(&s->lock, &flags);
	{
		ss7_unbufcall((str_t *) s);
		sdl_timer_stop(s, tall);
		if ((*(s->prev) = s->next))
			s->next->prev = s->prev;
		s->next = NULL;
		s->prev = &s->next;
		sdl_put(s);
		s->oq->q_ptr = NULL;
		flushq(s->oq, FLUSHALL);
		s->oq = NULL;
		sdl_put(s);
		s->iq->q_ptr = NULL;
		flushq(s->iq, FLUSHALL);
		s->iq = NULL;
		sdl_put(s);
	}
	lis_spin_unlock_irqrestore(&s->lock, &flags);
	sdl_put(s);		/* final put */
}
STATIC struct sdl *sdl_get(struct sdl *s)
{
	atomic_inc(&s->refcnt);
	return (s);
}
STATIC void sdl_put(struct sdl *s)
{
	if (atomic_dec_and_test(&s->refcnt)) {
		kmem_cache_free(sdl_priv_cachep, s);
		printd(("%s: %p: freed sdl private structure\n", SDL_MOD_NAME, s));
	}
}

/*
 *  =======================================================================
 *
 *  LiS Module Initialization (For unregistered driver.)
 *
 *  =======================================================================
 */
STATIC int sdl_initialized = 0;
STATIC void sdl_init(void)
{
	unless(sdl_initialized > 0, return);
	cmn_err(CE_NOTE, SDL_BANNER);	/* console splash */
	if ((sdl_initialized = sdl_init_caches())) {
		cmn_err(CE_PANIC, "%s: ERROR: could not allocate caches", SDL_MOD_NAME);
	} else if ((sdl_initialized = lis_register_strmod(&sdl_info, SDL_MOD_NAME)) < 0) {
		cmn_err(CE_WARN, "%s: couldn't register module", SDL_MOD_NAME);
		sdl_free_caches();
	}
	return;
}
STATIC void sdl_terminate(void)
{
	ensure(sdl_initialized > 0, return);
	if ((sdl_initialized = lis_unregister_strmod(&sdl_info)) < 0) {
		cmn_err(CE_PANIC, "%s: couldn't unregister module", SDL_MOD_NAME);
	} else {
		sdl_free_caches();
	}
	return;
}

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


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

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

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