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/hdlc/hdlc.c


File /code/strss7/drivers/hdlc/hdlc.c



#ident "@(#) $RCSfile: hdlc.c,v $ $Name:  $($Revision: 0.8.2.2 $) $Date: 2003/06/24 19:21:48 $"

static char const ident[] = "$RCSfile: hdlc.c,v $ $Name:  $($Revision: 0.8.2.2 $) $Date: 2003/06/24 19:21:48 $";

/*
 *  This is an HDLC (High-Level Data Link Control) module which
 *  provides HDLC capabilities when pushed over a Channel Interface (CHI)
 *  stream.  The module provides the Communications Device Interface (CDI).
 *  This supports both STYLE1 and STYLE2 providers.  Attach addresses are
 *  passed through to the Channel Interface (CHI) stream below permitting this
 *  module to be pushed immediately over a freshly opened Channel Interface
 *  (CHI) stream.
 *
 *  The HDLC is intended to be linked under the Q920 multiplexing driver to be
 *  accessed by LAPD and LAPF modules that are subsequently pushed under IDSN
 *  and Frame Relay drivers.
 */

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

#include <sys/cdi.h>
#include <sys/cdi_hdlc.h>
#include <ss7/chi.h>
#include <ss7/chi_ioctl.h>
#include <ss7/lmi.h>
#include <ss7/lmi_ioctl.h>
#include <ss7/hdlc_ioctl.h>

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

#define HDLC_DESCRIP	"ISO 3309/4335 HDLC: (High-Level Data Link Control) STREAMS MODULE."
#define HDLC_COPYRIGHT	"Copyright (c) 1997-2003 OpenSS7 Corporation.  All Rights Reserved."
#define HDLC_DEVICES	"Supports OpenSS7 Channel Drivers."
#define HDLC_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define HDLC_LICENSE	"GPL"
#define HDLC_BANNER	HDLC_DESCRIP   "\n" \
			HDLC_COPYRIGHT "\n" \
			HDLC_DEVICES   "\n" \
			HDLC_CONTACT   "\n"

MODULE_AUTHOR(HDLC_CONTACT);
MODULE_DESCRIPTION(HDLC_DESCRIP);
MODULE_SUPPORTED_DEVICE(HDLC_DEVICES);
#ifdef MODULE_LICENSE
MODULE_LICENSE(HDLC_LICENSE);
#endif

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

#define HDLC_MOD_ID	0
#define HDLC_MOD_NAME	"cd"

STATIC struct module_info cd_winfo = {
	mi_idnum:HDLC_MOD_ID,			/* Module ID number */
	mi_idname:HDLC_MOD_NAME "-wr",		/* Module name */
	mi_minpsz:(1),				/* Min packet size accepted */
	mi_maxpsz:INFPSZ,			/* Max packet size accepted */
	mi_hiwat:(1),				/* Hi water mark */
	mi_lowat:(0),				/* Lo water mark */
};

STATIC struct module_info cd_rinfo = {
	mi_idnum:HDLC_MOD_ID,			/* Module ID number */
	mi_idname:HDLC_MOD_NAME "-rd",		/* Module name */
	mi_minpsz:(1),				/* Min packet size accepted */
	mi_maxpsz:INFPSZ,			/* Max packet size accepted */
	mi_hiwat:(1),				/* Hi water mark */
	mi_lowat:(0),				/* Lo water mark */
};

STATIC int cd_open(queue_t *, dev_t *, int, int, cred_t *);
STATIC int cd_close(queue_t *, int, cred_t *);

STATIC struct qinit cd_rinit = {
	qi_putp:ss7_oput,			/* Read put (message from below) */
	qi_srvp:ss7_osrv,			/* Read queue service */
	qi_qopen:cd_open,			/* Each open */
	qi_qclose:cd_close,			/* Last close */
	qi_minfo:&cd_rinfo,			/* Information */
};

STATIC struct qinit cd_winit = {
	qi_putp:ss7_iput,			/* Write put (message from above) */
	qi_srvp:ss7_isrv,			/* Write queue service */
	qi_minfo:&cd_winfo,			/* Information */
};

STATIC struct streamtab hdlc_info = {
	st_rdinit:&cd_rinit,			/* Upper read queue */
	st_wrinit:&cd_winit,			/* Upper write queue */
};

STATIC int cd_r_prim(queue_t *q, mblk_t *mp);
STATIC int cd_w_prim(queue_t *q, mblk_t *mp);

STATIC void cd_wakeup(queue_t *q);

/*
 *  ========================================================================
 *
 *  Private structure
 *
 *  ========================================================================
 */
struct hdlc_path {
	uint residue;				/* residue bits */
	uint rbits;				/* number of residue bits */
	ushort bcc;				/* crc for message */
	uint state;				/* state */
	uint mode;				/* path mode */
	uint type;				/* path frame type */
	uint bytes;				/* number of whole bytes */
	mblk_t *msg;				/* message */
	mblk_t *nxt;				/* message chain block */
	mblk_t *cmp;				/* repeat/compress buffer */
	uint repeat;				/* repeat/compress count */
};

struct cd {
	STR_DECLARATION (struct cd);		/* stream declaration */
	struct hdlc_path tx;			/* transmit path variables */
	struct hdlc_path rx;			/* receive path variables */
	uint rx_octets;				/* no received octets */
	ulong ppa;				/* physical point of attachment */
	ulong sigs;				/* modem/alarm signals */
	lmi_option_t option;			/* HDLC protocol options */
	struct {
		cd_info_ack_t cd;		/* CD information */
		CH_info_ack_t ch;		/* CH information */
	} info;
	CH_parms_circuit_t parm;		/* CH circuit parameters */
	hdlc_timers_t timers;			/* HDLC timers */
	hdlc_statem_t statem;			/* HDLC state machine variables */
	hdlc_config_t config;			/* HDLC configuration options */
	hdlc_notify_t notify;			/* HDLC notification options */
	hdlc_stats_t stats;			/* HDLC statistics */
	hdlc_stats_t stamp;			/* HDLC statistics timestamps */
	hdlc_stats_t statsp;			/* HDLC statistics periods */
};

#define PRIV(__q) ((struct cd *)(__q)->q_ptr)

#define HDLC_F_DISCONNECTED	0x00000001	/* autonomous disconnect */

#ifndef CD_UNINIT
#define CD_UNINIT	(-1UL)
#endif

/*
 *  State Flags
 */
#define CDF_UNATTACHED		(1<<CD_UNATTACHED     )	/* No PPA attached */
#define CDF_UNUSABLE		(1<<CD_UNUSABLE       )	/* PPA cannot be used */
#define CDF_DISABLED		(1<<CD_DISABLED       )	/* PPA attached */
#define CDF_ENABLE_PENDING	(1<<CD_ENABLE_PENDING )	/* Waiting ack of enable req */
#define CDF_ENABLED		(1<<CD_ENABLED        )	/* Awaiting use */
#define CDF_READ_ACTIVE		(1<<CD_READ_ACTIVE    )	/* Input section enabled; disabled after data arrives */
#define CDF_INPUT_ALLOWED	(1<<CD_INPUT_ALLOWED  )	/* Input section permanently enabled */
#define CDF_DISABLE_PENDING	(1<<CD_DISABLE_PENDING)	/* Waiting ack of disable req */
#define CDF_OUTPUT_ACTIVE	(1<<CD_OUTPUT_ACTIVE  )	/* Output section active only */
#define	CDF_XRAY		(1<<CD_XRAY           )	/* Xray-ing another ppa */

/*
 *  -------------------------------------------------------------------------
 *
 *  Private structure allocation and deallocation
 *
 *  -------------------------------------------------------------------------
 */
STATIC kmem_cache_t *hdlc_priv_cachep = NULL;
STATIC int hdlc_init_caches(void)
{
	if (!hdlc_priv_cachep &&
	    !(hdlc_priv_cachep =
	      kmem_cache_create("hdlc_priv_cachep", sizeof(struct cd), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) {
		cmn_err(CE_PANIC, "%s: Cannot allocate hdlc_priv_cachep", __FUNCTION__);
		return (-ENOMEM);
	}
	printd(("%s: Allocated/selected private structure cache\n", HDLC_MOD_NAME));
	return (0);
}
STATIC void hdlc_free_caches(void)
{
	if (hdlc_priv_cachep) {
		if (kmem_cache_destroy(hdlc_priv_cachep))
			cmn_err(CE_WARN, "%s: did not destroy hdlc_priv_cachep.", __FUNCTION__);
		else
			printd(("cd: destroyed hdlc_priv_cachep\n"));
	}
	return;
}
STATIC struct cd *cd_get(struct cd *cd)
{
	atomic_inc(&cd->refcnt);
	return (cd);
}
STATIC void cd_put(struct cd *cd)
{
	if (atomic_dec_and_test(&cd->refcnt)) {
		kmem_cache_free(hdlc_priv_cachep, cd);
		printd(("%s: %p: freed cd private structure\n", HDLC_MOD_NAME, cd));
	}
	return;
}
STATIC void hdlc_free_priv(queue_t *q)
{
	struct cd *cd = PRIV(q);
	int flags = 0;
	ensure(cd, return);
	lis_spin_lock_irqsave(&cd->lock, &flags);
	{
		ss7_unbufcall((str_t *) cd);
		if (cd->tx.msg && cd->tx.msg != cd->tx.cmp)
			freemsg(xchg(&cd->tx.msg, NULL));
		if (cd->tx.cmp)
			freemsg(xchg(&cd->tx.cmp, NULL));
		if (cd->rx.msg)
			freemsg(xchg(&cd->rx.msg, NULL));
		if (cd->rx.nxt)
			freemsg(xchg(&cd->rx.nxt, NULL));
		if (cd->rx.cmp)
			freemsg(xchg(&cd->rx.cmp, NULL));
		if ((*cd->prev = cd->next))
			cd->next->prev = cd->prev;
		cd->next = NULL;
		cd->prev = &cd->next;
		cd_put(cd);
		flushq(xchg(&cd->oq, NULL), FLUSHALL);
		cd_put(cd);
		flushq(xchg(&cd->iq, NULL), FLUSHALL);
		cd_put(cd);
	}
	lis_spin_unlock_irqrestore(&cd->lock, &flags);
	cd_put(cd);		/* final put */
	return;
}
STATIC struct cd *hdlc_alloc_priv(queue_t *q, struct cd **hpp, dev_t *devp, cred_t *crp)
{
	struct cd *cd;
	if ((cd = kmem_cache_alloc(hdlc_priv_cachep, SLAB_ATOMIC))) {
		printd(("cd: allocated module private structure\n"));
		bzero(cd, sizeof(*cd));
		cd->put = &cd_put;	/* set put method */
		cd_get(cd);	/* first get */
		cd->u.dev.cmajor = getmajor(*devp);
		cd->u.dev.cminor = getminor(*devp);
		cd->cred = *crp;
		(cd->oq = RD(q))->q_ptr = cd_get(cd);
		(cd->iq = WR(q))->q_ptr = cd_get(cd);
		lis_spin_lock_init(&cd->qlock, "cd-queue-lock");
		cd->o_prim = &cd_r_prim;
		cd->i_prim = &cd_w_prim;
		cd->o_wakeup = &cd_wakeup;
		cd->i_wakeup = &cd_wakeup;
		cd->i_version = 1;
		cd->i_state = CD_UNINIT;
		cd->i_style = CD_STYLE2;
		lis_spin_lock_init(&cd->qlock, "cd-priv_lock");
		if ((cd->next = *hpp))
			cd->next->prev = &cd->next;
		cd->prev = hpp;
		*hpp = cd_get(cd);
		printd(("%s: %p: linked module private structure\n", HDLC_MOD_NAME, cd));
		printd(("%s: %p: setting module private structure defaults\n", HDLC_MOD_NAME, cd));
		cd->info.cd.cd_primitive = CD_INFO_ACK;
		cd->info.cd.cd_state = CD_UNINIT;
		cd->info.cd.cd_max_sdu = 260;
		cd->info.cd.cd_min_sdu = 3;
		cd->info.cd.cd_class = CD_HDLC;
		cd->info.cd.cd_duplex = CD_FULLDUPLEX;
		cd->info.cd.cd_features = CD_CANREAD | CD_AUTOALLOW;
		cd->info.cd.cd_addr_length = 0;
		cd->info.cd.cd_ppa_style = CD_STYLE2;	/* for now */
		/* assume default circuit info */
		cd->info.ch.ch_primitive = CH_INFO_ACK;
		cd->info.ch.ch_addr_length = 0;
		cd->info.ch.ch_addr_offset = 0;
		cd->info.ch.ch_parm_length = 0;
		cd->info.ch.ch_parm_offset = 0;
		cd->info.ch.ch_prov_flags = 0;
		cd->info.ch.ch_style = CH_STYLE2;
		cd->info.ch.ch_version = 1;
		cd->info.ch.ch_state = CHS_UNINIT;
		/* assume default circuit parameters */
		cd->parm.cp_type = CH_PARMS_CIRCUIT;
		cd->parm.cp_block_size = 64;	/* bits */
		cd->parm.cp_encoding = CH_ENCODING_NONE;
		cd->parm.cp_sample_size = 8;	/* bits */
		cd->parm.cp_rate = CH_RATE_8000;	/* samples per second */
		cd->parm.cp_tx_channels = 1;
		cd->parm.cp_rx_channels = 1;
		cd->parm.cp_opt_flags = CH_PARM_OPT_CLRCH;
		todo(("set module defaults\n"));
	} else
		ptrace(("%s: ERROR: Could not allocate module private structure\n", HDLC_MOD_NAME));
	return (cd);
}

/*
 *  ========================================================================
 *
 *  FUNCTIONS
 *
 *  ========================================================================
 */

/*
 *  CD State Changes
 */
#ifdef _DEBUG
STATIC INLINE const char *cd_state_name(ulong state)
{
	switch (state) {
	case CD_UNINIT:
		return ("CD_UNINIT");
	case CD_UNATTACHED:
		return ("CD_UNATTACHED");
	case CD_UNUSABLE:
		return ("CD_UNUSABLE");
	case CD_DISABLED:
		return ("CD_DISABLED");
	case CD_ENABLE_PENDING:
		return ("CD_ENABLE_PENDING");
	case CD_ENABLED:
		return ("CD_ENABLED");
	case CD_READ_ACTIVE:
		return ("CD_READ_ACTIVE");
	case CD_INPUT_ALLOWED:
		return ("CD_INPUT_ALLOWED");
	case CD_DISABLE_PENDING:
		return ("CD_DISABLE_PENDING");
	case CD_OUTPUT_ACTIVE:
		return ("CD_OUTPUT_ACTIVE");
	case CD_XRAY:
		return ("CD_XRAY");
	default:
		swerr();
		return ("CD_????");
	}
}
#endif
STATIC INLINE ulong cd_get_state(struct cd * cd)
{
	return cd->i_state;
}
STATIC INLINE ulong cd_tst_state(struct cd * cd, ulong flags)
{
	return ((1 << cd_get_state(cd)) & flags);
}
STATIC INLINE ulong cd_set_state(struct cd * cd, ulong newstate)
{
	ulong oldstate = cd->i_state;
	(void) oldstate;
	cd->info.cd.cd_state = cd->i_state = newstate;
	printd(("%s: %p: %s <- %s\n", HDLC_MOD_NAME, cd, cd_state_name(newstate), cd_state_name(oldstate)));
	return (newstate);
}

/*
 *  CH State Changes
 */
#ifdef _DEBUG
STATIC INLINE const char *ch_state_name(ulong state)
{
	switch (state) {
	case CHS_UNINIT:
		return ("CHS_UNINIT");
	case CHS_UNUSABLE:
		return ("CHS_UNUSABLE");
	case CHS_DETACHED:
		return ("CHS_DETACHED");
	case CHS_WACK_AREQ:
		return ("CHS_WACK_AREQ");
	case CHS_WACK_UREQ:
		return ("CHS_ATTACHED");
	case CHS_WACK_EREQ:
		return ("CHS_WACK_EREQ");
	case CHS_WCON_EREQ:
		return ("CHS_WCON_EREQ");
	case CHS_WACK_RREQ:
		return ("CHS_WACK_RREQ");
	case CHS_WCON_RREQ:
		return ("CHS_WCON_RREQ");
	case CHS_ENABLED:
		return ("CHS_ENABLED");
	case CHS_WACK_CREQ:
		return ("CHS_WACK_CREQ");
	case CHS_WCON_CREQ:
		return ("CHS_WCON_CREQ");
	case CHS_WACK_DREQ:
		return ("CHS_WACK_DREQ");
	case CHS_WCON_DREQ:
		return ("CHS_WCON_DREQ");
	case CHS_CONNECTED:
		return ("CHS_CONNECTED");
	default:
		swerr();
		return ("CHS_????");
	}
}
#endif
STATIC INLINE ulong ch_get_state(struct cd * cd)
{
	return cd->state;
}
STATIC INLINE ulong ch_set_state(struct cd * cd, ulong newstate)
{
	ulong oldstate = cd->state;
	(void) oldstate;
	cd->info.ch.ch_state = cd->state = newstate;
	printd(("%s: %p: %s <- %s\n", HDLC_MOD_NAME, cd, ch_state_name(newstate), ch_state_name(oldstate)));
	return (newstate);
}

/*
 *  ========================================================================
 *
 *  PRIMITIVES
 *
 *  ========================================================================
 */
/*
 *  ------------------------------------------------------------------------
 *
 *  Primitives sent upstream
 *
 *  ------------------------------------------------------------------------
 */
/*
 *  M_ERROR
 *  -----------------------------------
 */
STATIC INLINE int m_error(queue_t *q, int err)
{
	struct cd *cd = PRIV(q);
	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;
		cd_set_state(cd, CD_UNUSABLE);
		ch_set_state(cd, CHS_UNUSABLE);
		printd(("%s: %p: <- M_ERROR\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  M_HANGUP
 *  -----------------------------------
 */
STATIC INLINE int m_hangup(queue_t *q, int err)
{
	struct cd *cd = PRIV(q);
	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;
		cd_set_state(cd, CD_UNUSABLE);
		ch_set_state(cd, CHS_UNUSABLE);
		printd(("%s: %p: <- M_HANGUP\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CD_INFO_ACK		0x01 - Information acknowledgement
 *  -----------------------------------------------------------
 */
STATIC INLINE int cd_info_ack(queue_t *q, struct cd *cd)
{
	mblk_t *mp;
	cd_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 = cd->info.cd;
		printd(("%s: %p: <- CD_INFO_ACK\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CD_OK_ACK		0x06 - Success acknowledgement
 *  -----------------------------------------------------------
 */
STATIC INLINE int cd_ok_ack(queue_t *q, struct cd *cd, ulong prim)
{
	mblk_t *mp;
	cd_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->cd_primitive = CD_OK_ACK;
		p->cd_correct_primitive = prim;
		switch (prim) {
		case CD_ABORT_OUTPUT_REQ:
			if (cd_get_state(cd) == CD_OUTPUT_ACTIVE)
				p->cd_state = cd_set_state(cd, CD_ENABLED);
			else
				p->cd_state = cd_get_state(cd);
			break;
		case CD_ALLOW_INPUT_REQ:
			p->cd_state = cd_set_state(cd, CD_INPUT_ALLOWED);
			break;
		case CD_ATTACH_REQ:
			p->cd_state = cd_set_state(cd, CD_DISABLED);
			break;
		case CD_DETACH_REQ:
			p->cd_state = cd_set_state(cd, CD_UNATTACHED);
			break;
		case CD_HALT_INPUT_REQ:
			p->cd_state = cd_set_state(cd, CD_ENABLED);
			break;
		case CD_MODEM_SIG_REQ:
			p->cd_state = cd_set_state(cd, CD_ENABLED);
			break;
		case CD_MUX_NAME_REQ:
			p->cd_state = cd_set_state(cd, CD_ENABLED);
			break;
		default:
			p->cd_state = cd_get_state(cd);
			swerr();
			break;
		}
		printd(("%s: %p: <- CD_OK_ACK\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CD_ERROR_ACK	0x07 - Error acknowledgement
 *  -----------------------------------------------------------
 */
STATIC INLINE int cd_error_ack(queue_t *q, struct cd *cd, ulong prim, ulong error, ulong reason)
{
	mblk_t *mp;
	cd_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->cd_primitive = CD_ERROR_ACK;
		p->cd_error_primitive = prim;
		p->cd_errno = error;
		p->cd_explanation = reason;
		if (error == CD_FATALERR)
			cd_set_state(cd, CD_UNUSABLE);
		else
			cd_set_state(cd, cd->i_oldstate);
		p->cd_state = cd_get_state(cd);
		printd(("%s: %p: <- CD_ERROR_ACK\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CD_ENABLE_CON	0x08 - Enable confirmation
 *  -----------------------------------------------------------
 */
STATIC INLINE int cd_enable_con(queue_t *q, struct cd *cd)
{
	mblk_t *mp;
	cd_enable_con_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->cd_primitive = CD_ENABLE_CON;
		p->cd_state = cd_set_state(cd, CD_ENABLED);
		printd(("%s: %p: <- CD_ENABLE_CON\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CD_DISABLE_CON	0x09 - Disable confirmation
 *  -----------------------------------------------------------
 */
STATIC INLINE int cd_disable_con(queue_t *q, struct cd *cd)
{
	mblk_t *mp;
	cd_disable_con_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->cd_primitive = CD_DISABLE_CON;
		p->cd_state = cd_set_state(cd, CD_DISABLED);
		printd(("%s: %p: <- CD_DISABLE_CON\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CD_ERROR_IND	0x0a - Error indication
 *  -----------------------------------------------------------
 */
STATIC INLINE int cd_error_ind(queue_t *q, struct cd *cd, ulong error, ulong reason, ulong state, mblk_t *dp)
{
	mblk_t *mp;
	cd_error_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->cd_primitive = CD_ERROR_IND;
		p->cd_state = cd_set_state(cd, state);
		p->cd_errno = error;
		p->cd_explanation = reason;
		mp->b_cont = dp;
		printd(("%s: %p: <- CD_ERROR_IND\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CD_UNITDATA_ACK	0x0f - Data send acknowledgement
 *  -----------------------------------------------------------
 */
STATIC INLINE int cd_uintdata_ack(queue_t *q, struct cd *cd)
{
	mblk_t *mp;
	cd_unitdata_ack_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		mp->b_band = 2;
		p = (typeof(p)) mp->b_wptr++;
		p->cd_primitive = CD_UNITDATA_ACK;
		p->cd_state = cd_get_state(cd);
		printd(("%s: %p: <- CD_UNITDATA_ACK\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CD_UNITDATA_IND	0x10 - Data receive indication
 *  -----------------------------------------------------------
 */
STATIC INLINE int cd_unitdata_ind(queue_t *q, struct cd *cd, mblk_t *dp)
{
	mblk_t *mp;
	cd_unitdata_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->cd_primitive = CD_UNITDATA_IND;
		p->cd_state = cd_get_state(cd);
		p->cd_src_addr_length = 0;
		p->cd_src_addr_offset = 0;
		p->cd_addr_type = CD_IMPLICIT;
		p->cd_priority = 0;
		p->cd_dest_addr_length = 0;
		p->cd_dest_addr_offset = 0;
		mp->b_cont = dp;
		printd(("%s: %p: <- CD_UNITDATA_IND\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_ABSORBED);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CD_BAD_FRAME_IND	0x14 - frame w/error (Gcom extension)
 *  -----------------------------------------------------------
 */
STATIC INLINE int cd_bad_frame_ind(queue_t *q, struct cd *cd, ulong error, mblk_t *dp)
{
	mblk_t *mp;
	cd_bad_frame_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->cd_primitive = CD_BAD_FRAME_IND;
		p->cd_state = cd_get_state(cd);
		p->cd_error = error;
		mp->b_cont = dp;
		printd(("%s: %p: <- CD_BAD_FRAME_IND\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_ABSORBED);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CD_MODEM_SIG_IND	0x16 - Report modem signal state (Gcom)
 *  -----------------------------------------------------------
 */
STATIC INLINE int cd_modem_sig_ind(queue_t *q, struct cd *cd, ulong sigs)
{
	mblk_t *mp;
	cd_modem_sig_ind_t *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		mp->b_band = 1;
		p = (typeof(p)) mp->b_wptr++;
		p->cd_primitive = CD_MODEM_SIG_IND;
		p->cd_sigs = sigs;
		printd(("%s: %p: <- CD_MODEM_SIG_IND\n", HDLC_MOD_NAME, cd));
		putnext(cd->oq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  ------------------------------------------------------------------------
 *
 *  PRIMITIVES Sent Downstream
 *
 *  ------------------------------------------------------------------------
 */

/*
 *  CH_INFO_REQ
 *  -----------------------------------
 */
STATIC INLINE int ch_info_req(queue_t *q, struct cd *cd)
{
	mblk_t *mp;
	struct CH_info_req *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = (typeof(p)) mp->b_wptr++;
		p->ch_primitive = CH_INFO_REQ;
		printd(("%s: %p: CH_INFO_REQ ->\n", HDLC_MOD_NAME, cd));
		putnext(cd->iq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CH_OPTMGMT_REQ
 *  -----------------------------------
 */
STATIC INLINE int ch_optmgmt_req(queue_t *q, struct cd *cd)
{
	mblk_t *mp;
	struct CH_optmgmt_req *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = (typeof(p)) mp->b_wptr++;
		p->ch_primitive = CH_OPTMGMT_REQ;
		printd(("%s: %p: CH_OPTMGMT_REQ ->\n", HDLC_MOD_NAME, cd));
		putnext(cd->iq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CH_ATTACH_REQ
 *  -----------------------------------
 */
STATIC INLINE int ch_attach_req(queue_t *q, struct cd *cd, size_t add_len, caddr_t add_ptr, ulong flags)
{
	mblk_t *mp;
	struct CH_attach_req *p;
	if ((mp = ss7_allocb(q, sizeof(*p) + add_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = (typeof(p)) mp->b_wptr++;
		p->ch_primitive = CH_ATTACH_REQ;
		p->ch_addr_length = add_len;
		p->ch_addr_offset = add_len ? sizeof(*p) : 0;
		p->ch_flags = flags;
		if (add_len) {
			bcopy(add_ptr, mp->b_wptr, add_len);
			mp->b_wptr += add_len;
		}
		ch_set_state(cd, CHS_WACK_AREQ);
		printd(("%s: %p: CH_ATTACH_REQ ->\n", HDLC_MOD_NAME, cd));
		putnext(cd->iq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CH_ENABLE_REQ
 *  -----------------------------------
 */
STATIC INLINE int ch_enable_req(queue_t *q, struct cd *cd)
{
	mblk_t *mp;
	struct CH_enable_req *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = (typeof(p)) mp->b_wptr++;
		p->ch_primitive = CH_ENABLE_REQ;
		ch_set_state(cd, CHS_WACK_EREQ);
		printd(("%s: %p: CH_ENABLE_REQ ->\n", HDLC_MOD_NAME, cd));
		putnext(cd->iq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CH_CONNECT_REQ
 *  -----------------------------------
 */
STATIC INLINE int ch_connect_req(queue_t *q, struct cd *cd, ulong flags)
{
	mblk_t *mp;
	struct CH_connect_req *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = (typeof(p)) mp->b_wptr++;
		p->ch_primitive = CH_CONNECT_REQ;
		p->ch_conn_flags = flags;
		p->ch_slot = 0;
		ch_set_state(cd, CHS_WACK_CREQ);
		printd(("%s: %p: CH_CONNECT_REQ ->\n", HDLC_MOD_NAME, cd));
		putnext(cd->iq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CH_DATA_REQ
 *  -----------------------------------
 */
STATIC INLINE int ch_data_req(queue_t *q, struct cd *cd, mblk_t *dp)
{
	mblk_t *mp;
	struct CH_data_req *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = (typeof(p)) mp->b_wptr++;
		p->ch_primitive = CH_DATA_REQ;
		p->ch_slot = 0;
		mp->b_cont = dp;
		printd(("%s: %p: CH_DATA_REQ ->\n", HDLC_MOD_NAME, cd));
		putnext(cd->iq, mp);
		return (QR_ABSORBED);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CH_DISCONNECT_REQ
 *  -----------------------------------
 */
STATIC INLINE int ch_disconnect_req(queue_t *q, struct cd *cd, ulong flags)
{
	mblk_t *mp;
	struct CH_disconnect_req *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = (typeof(p)) mp->b_wptr++;
		p->ch_primitive = CH_DISCONNECT_REQ;
		p->ch_conn_flags = flags;
		p->ch_slot = 0;
		ch_set_state(cd, CHS_WACK_DREQ);
		printd(("%s: %p: CH_DISCONNECT_REQ ->\n", HDLC_MOD_NAME, cd));
		putnext(cd->iq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CH_DISABLE_REQ
 *  -----------------------------------
 */
STATIC INLINE int ch_disable_req(queue_t *q, struct cd *cd)
{
	mblk_t *mp;
	struct CH_disable_req *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = (typeof(p)) mp->b_wptr++;
		p->ch_primitive = CH_DISABLE_REQ;
		ch_set_state(cd, CHS_WACK_RREQ);
		printd(("%s: %p: CH_DISABLE_REQ ->\n", HDLC_MOD_NAME, cd));
		putnext(cd->iq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  CH_DETACH_REQ
 *  -----------------------------------
 */
STATIC INLINE int ch_detach_req(queue_t *q, struct cd *cd)
{
	mblk_t *mp;
	struct CH_detach_req *p;
	if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = (typeof(p)) mp->b_wptr++;
		p->ch_primitive = CH_DETACH_REQ;
		ch_set_state(cd, CHS_WACK_UREQ);
		printd(("%s: %p: CH_DETACH_REQ ->\n", HDLC_MOD_NAME, cd));
		putnext(cd->iq, mp);
		return (QR_DONE);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  =========================================================================
 *
 *  PROTOCOL STATE MACHINE FUNCTIONS
 *
 *  =========================================================================
 */

/*
 *  ========================================================================
 *
 *  Soft-HDLC
 *
 *  ========================================================================
 */
#define HDLC_TX_STATES	5
#define HDLC_RX_STATES	14

#define HDLC_TX_BUFSIZE	PAGE_SIZE
#define HDLC_RX_BUFSIZE	PAGE_SIZE

#define HDLC_CRC_TABLE_LENGTH	512
#define HDLC_TX_TABLE_LENGTH	(2* HDLC_TX_STATES * 256)
#define HDLC_RX_TABLE_LENGTH	(2* HDLC_RX_STATES * 256)

typedef struct tx_entry {
	uint bit_string:10;			/* the output string */
	uint bit_length:4;			/* length in excess of 8 bits of output string */
	uint state:3;				/* new state */
} tx_entry_t __attribute__ ((packed));

typedef struct rx_entry {
	uint bit_string:16;
	uint bit_length:4;
	uint state:4;
	uint sync:1;
	uint hunt:1;
	uint flag:1;
	uint idle:1;
} rx_entry_t __attribute__ ((packed));

typedef uint16_t bc_entry_t;

STATIC bc_entry_t *bc_table = NULL;
STATIC tx_entry_t *tx_table = NULL;
STATIC rx_entry_t *rx_table = NULL;
STATIC rx_entry_t *rx_table7 = NULL;

STATIC size_t bc_order = 0;
STATIC size_t tx_order = 0;
STATIC size_t rx_order = 0;

STATIC INLINE tx_entry_t *tx_index8(uint j, uint k)
{
	return &tx_table[(j << 8) | k];
}
STATIC INLINE rx_entry_t *rx_index7(uint j, uint k)
{
	return &rx_table7[(j << 8) | k];
}
STATIC INLINE rx_entry_t *rx_index8(uint j, uint k)
{
	return &rx_table[(j << 8) | k];
}

/*
 *  REV
 *  -----------------------------------
 *  Reverse bits.  (There must be a better way to do this...)
 */
STATIC INLINE uchar cd_rev(uchar byte)
{
	int i;
	uchar output = 0;
	for (i = 0; i < 8; i++) {
		output <<= 1;
		if (byte & 0x01)
			output |= 1;
		byte >>= 1;
	}
	return (output);
}

/*
 *  TX BUFFER
 *  ----------------------------------------
 *  Pick up another TX buffer from the queue or a repeat frame.
 */
STATIC INLINE mblk_t *cd_tx_buffer(queue_t *q, struct cd *cd)
{
	mblk_t *mp, *dp = NULL;
	for (mp = cd->iq->q_first; mp; mp = mp->b_next) {
		switch (mp->b_datap->db_type) {
		case M_DATA:
			dp = mp;
			rmvq(cd->iq, dp);
			break;
		case M_PROTO:
			if (*((ulong *) mp->b_rptr) != CD_UNITDATA_REQ)
				continue;
			dp = mp->b_cont;
			rmvq(cd->iq, mp);
			freeb(mp);
			break;
		default:
			continue;
		}
		break;
	}
	return (dp);
}

/*
 *  TX BITSTUFF
 *  ----------------------------------------
 *  Bitstuff an octet and shift residue for output.
 */
STATIC INLINE void cd_tx_bitstuff(struct hdlc_path *tx, unsigned char byte)
{
	tx_entry_t *t = tx_index8(tx->state, byte);
	tx->state = t->state;
	tx->residue |= t->bit_string << tx->rbits;
	tx->rbits += t->bit_length + 8;
}

#define TX_MODE_IDLE	0	/* generating mark idle */
#define TX_MODE_FLAG	1	/* generating flag idle */
#define TX_MODE_BOF	2	/* generating bof flag */
#define TX_MODE_MOF	3	/* generating frames */
#define TX_MODE_BCC	4	/* generating bcc bytes */
/*
 *  TX BLOCK
 *  ----------------------------------------
 *  Generate blocks for transmission.  We generate entire transmit blocks.  If
 *  there are not sufficient messages to build the transmit blocks we will
 *  idle flags.
 */
STATIC int cd_tx_block(queue_t *q, struct cd *cd)
{
	mblk_t *bp;
	struct hdlc_path *tx = &cd->tx;
	struct hdlc_stats *stats = &cd->stats;
	int bits = cd->parm.cp_sample_size;
	int blks = (cd->parm.cp_block_size + 7) >> 3;
	int mask = (1 << bits) - 1;
	if (!(bp = ss7_allocb(q, blks, BPRI_MED)))
		goto enobufs;	/* bufcall will bring us back */
	if (tx->mode == TX_MODE_IDLE || tx->mode == TX_MODE_FLAG) {
		if (!tx->nxt) {
		      next_message:
			if (tx->msg && tx->msg != tx->cmp)
				freemsg(tx->msg);
			if ((tx->msg = tx->nxt = cd_tx_buffer(q, cd)))
				tx->mode = TX_MODE_BOF;
		}
	}
	/* check if transmission block complete */
	while (bp->b_wptr < bp->b_rptr + blks) {
		/* drain residue bits, if necessary */
		if (tx->rbits >= bits) {
		      drain_rbits:
			/* drain residue bits */
			*bp->b_wptr++ = cd_rev(tx->residue & mask);
			tx->residue >>= bits;
			tx->rbits -= bits;
			continue;
		}
		switch (tx->mode) {
		case TX_MODE_IDLE:
			/* mark idle */
			tx->residue |= 0xff << tx->rbits;
			tx->rbits += 8;
			goto drain_rbits;
		case TX_MODE_FLAG:
			/* idle flags */
			tx->residue |= 0x7e << tx->rbits;
			tx->rbits += 8;
			goto drain_rbits;
		case TX_MODE_BOF:
			/* add opening flag (also closing flag) */
			switch (cd->config.f) {
			case HDLC_FLAGS_ONE:
				tx->residue |= 0x7e << tx->rbits;
				tx->rbits += 8;
				break;
			case HDLC_FLAGS_SHARED:
				tx->residue |= 0x3f7e << tx->rbits;
				tx->rbits += 15;
				break;
			case HDLC_FLAGS_TWO:
				tx->residue |= 0x7e7e << tx->rbits;
				tx->rbits += 16;
				break;
			default:
			case HDLC_FLAGS_THREE:
				tx->residue |= 0x7e7e7e << tx->rbits;
				tx->rbits += 24;
				break;
			}
			tx->state = 0;
			tx->bcc = 0x00ff;
			tx->mode = TX_MODE_MOF;
			goto drain_rbits;
		case TX_MODE_MOF:	/* transmit frame bytes */
			if (tx->nxt->b_rptr < tx->nxt->b_wptr || (tx->nxt = tx->nxt->b_cont)) {
				/* continuing in message */
				uint byte = *(tx->nxt->b_rptr)++;
				tx->bcc = (tx->bcc >> 8) ^ bc_table[(tx->bcc ^ byte) & 0x00ff];
				cd_tx_bitstuff(tx, byte);
				stats->tx_bytes++;
			} else {
				/* finished message: add 1st bcc byte */
				cd_tx_bitstuff(tx, tx->bcc & 0x00ff);
				tx->mode = TX_MODE_BCC;
			}
			goto drain_rbits;
		case TX_MODE_BCC:
			/* add 2nd bcc byte */
			cd_tx_bitstuff(tx, tx->bcc >> 8);
			stats->tx_frames++;
			tx->mode = TX_MODE_FLAG;
			goto next_message;
		}
		swerr();
	}
	putnext(q, bp);
	return (QR_DONE);
      enobufs:
	return (-ENOBUFS);
}

/*
 *  RX LINKB
 *  ----------------------------------------
 *  Link a buffer to existing message or create new message with buffer.
 */
STATIC INLINE void cd_rx_linkb(struct hdlc_path *rx)
{
	if (rx->msg)
		linkb(rx->msg, rx->nxt);
	else
		rx->msg = rx->nxt;
	rx->nxt = NULL;
	return;
}

#define RX_MODE_HUNT	0	/* hunting for flags */
#define RX_MODE_SYNC	1	/* between frames */
#define RX_MODE_MOF	2	/* middle of frame */
/*
 *  RX BLOCK
 *  ----------------------------------------
 *  Process a receive block for a channel or span.  We process all of the
 *  octets in the receive block.  Any complete messages will be delivered to
 *  the upper layer if the upper layer is not congested.  If the upper layer
 *  is congested we discard the message and indicate receive congestion.  The
 *  upper layer should be sensitive to its receive queue backlog and start
 *  sending SIB when required.  We do not use backenabling from the upper
 *  layer.  We merely start discarding complete messages when the upper layer
 *  is congested.
 */
STATIC INLINE int cd_rx_block(queue_t *q, struct cd *cd, mblk_t *dp)
{
	struct hdlc_path *rx = &cd->rx;
	struct hdlc_stats *stats = &cd->stats;
	int bits = cd->parm.cp_sample_size;
#if 0
	int N = (cd->info.cd.cd_min_sdu + 5) << 1;
#endif
	int err = 0;
	while (dp->b_rptr < dp->b_wptr) {
		rx_entry_t *r;
		if (bits == 8)
			r = rx_index8(rx->state, cd_rev(*dp->b_rptr++));
		else
			r = rx_index7(rx->state, cd_rev(*dp->b_rptr++));
		rx->state = r->state;
		switch (rx->mode) {
		case RX_MODE_MOF:
			if (!r->sync && r->bit_length) {
				rx->residue |= r->bit_string << rx->rbits;
				rx->rbits += r->bit_length;
			}
			if (r->flag)
				goto end_of_frame;
			if (r->hunt || r->idle)
				goto aborted;
			while (rx->rbits > 16) {
				if (rx->nxt && rx->nxt->b_wptr >= rx->nxt->b_datap->db_lim)
					cd_rx_linkb(rx);
				if (!rx->nxt && !(rx->nxt = ss7_allocb(q, FASTBUF, BPRI_HI)))
					goto enobufs;
				rx->bcc = (rx->bcc >> 8)
				    ^ bc_table[(rx->bcc ^ rx->residue) & 0x00ff];
				*(rx->nxt->b_wptr)++ = rx->residue;
				stats->rx_bytes++;
				rx->residue >>= 8;
				rx->rbits -= 8;
				rx->bytes++;
				if (rx->bytes > cd->info.cd.cd_max_sdu)
					goto frame_too_long;
			}
			break;
		      end_of_frame:
			if (rx->rbits != 16)
				goto residue_error;
			if ((~rx->bcc & 0xffff) != (rx->residue & 0xffff))
				goto crc_error;
			if (rx->bytes < cd->info.cd.cd_min_sdu)
				goto frame_too_short;
			cd_rx_linkb(rx);
			if (!canputnext(cd->oq))
				goto buffer_overflow;
			if ((err = cd_unitdata_ind(q, cd, rx->msg)) != QR_ABSORBED)
				goto error;
			rx->msg = NULL;
			stats->rx_frames++;
		      new_frame:
			rx->mode = RX_MODE_SYNC;
			rx->residue = 0;
			rx->rbits = 0;
			rx->bytes = 0;
			rx->bcc = 0xffff;
			if (!r->sync)
				break;
		      begin_frame:
			if (!r->bit_length)
				break;
			rx->mode = RX_MODE_MOF;
			rx->residue = r->bit_string;
			rx->rbits = r->bit_length;
			rx->bytes = 0;
			rx->bcc = 0x00ff;
			break;
		      frame_too_long:
			stats->rx_frame_too_long++;
			stats->rx_frame_errors++;
			err = CD_FORMAT;
			goto abort_frame;
		      buffer_overflow:
			stats->rx_buffer_overflows++;
			err = CD_OVERRUN;
			goto abort_frame;
		      aborted:
			stats->rx_aborts++;
			stats->rx_frame_errors++;
			err = CD_HDLC_ABORT;
			goto abort_frame;
		      frame_too_short:
			stats->rx_frame_too_short++;
			stats->rx_frame_errors++;
			err = CD_TOOSHORT;
			goto abort_frame;
		      crc_error:
			stats->rx_crc_errors++;
			err = CD_CRCERR;
			goto abort_frame;
		      residue_error:
			stats->rx_residue_errors++;
			stats->rx_frame_errors++;
			err = CD_INCOMPLETE;
			goto abort_frame;
		      abort_frame:
			if (rx->nxt)
				cd_rx_linkb(rx);
			if (cd_error_ind(q, cd, CD_BADFRAME, err, cd_get_state(cd), rx->msg))
				if (rx->msg)
					freemsg(rx->msg);
			rx->msg = NULL;
			stats->rx_frames_in_error++;
			if (r->flag)
				goto new_frame;
			rx->mode = RX_MODE_HUNT;
			stats->rx_sync_transitions++;
			cd->rx_octets = 0;
			break;
		case RX_MODE_SYNC:
			if (!r->hunt && !r->idle)
				goto begin_frame;
			rx->mode = RX_MODE_HUNT;
			stats->rx_sync_transitions++;
			cd->rx_octets = 0;
			break;
		case RX_MODE_HUNT:
			if (!r->flag) {
#if 0
				cd->rx_octets++;
				while (cd->rx_octets >= N) {
					if (cd_error_ind(q, cd, CD_BADFRAME, 0, cd_get_state(cd), NULL))
						break;
					stats->rx_sus_in_error++;
					cd->rx_octets -= N;
				}
				stats->rx_bits_octet_counted += 8;
#endif
				break;
			}
			stats->rx_sync_transitions++;
			goto new_frame;
		default:
			swerr();
			err = EIO;
			goto abort_frame;
		}
	}
	return (QR_ABSORBED);
      error:
	return (err);
      enobufs:
	return (-ENOBUFS);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Table allocation and generation
 *
 *  -------------------------------------------------------------------------
 *  All Soft HDLC lookup stables are generated at module load time.  This
 *  permits the tables to be page-aligned in kernel memory for maximum cache
 *  performance.
 */
/*
 *  BC (Block Check) CRC Table Entries:
 *  -----------------------------------
 *  RC tables perform CRC calculation on received bits after zero deletion and
 *  delimitation.
 */
STATIC bc_entry_t bc_table_value(int bit_string, int bit_length)
{
	int pos;
	for (pos = 0; pos < bit_length; pos++) {
		if (bit_string & 0x1)
			bit_string = (bit_string >> 1) ^ 0x8408;
		else
			bit_string >>= 1;
	}
	return (bit_string);
}

/*
 *  TX (Transmission) Table Entries:
 *  -----------------------------------
 *  TX table performs zero insertion on frame and CRC bit streams.
 */
STATIC tx_entry_t tx_table_valueN(int state, uint8_t byte, int len)
{
	tx_entry_t result = { 0, };
	int bit_mask = 1;
	result.state = state;
	result.bit_length = 0;
	while (len--) {
		if (byte & 0x1) {
			result.bit_string |= bit_mask;
			if (result.state++ == 4) {
				result.state = 0;
				result.bit_length++;
				bit_mask <<= 1;
			}
		} else
			result.state = 0;
		bit_mask <<= 1;
		byte >>= 1;
	}
	return result;
}
STATIC tx_entry_t tx_table_value(int state, uint8_t byte)
{
	return tx_table_valueN(state, byte, 8);
}

/*
 *  RX (Receive) Table Entries:
 *  -----------------------------------
 *  RX table performs zero deletion, flag and abort detection, BOF and EOF
 *  detection and residue on received bit streams.
 */
STATIC rx_entry_t rx_table_valueN(int state, uint8_t byte, int len)
{
	rx_entry_t result = { 0, };
	int bit_mask = 1;
	result.state = state;
	while (len--) {
		switch (result.state) {
		case 0:	/* */
			if (result.flag && !result.sync) {
				bit_mask = 1;
				result.bit_string = 0;
				result.bit_length = 0;
				result.sync = 1;
			}
			if (byte & 0x1) {
				result.state = 8;
			} else {
				result.state = 1;
			}
			break;
		case 1:	/* 0 */
			if (byte & 0x1) {
				result.state = 2;
			} else {
				bit_mask <<= 1;
				result.bit_length += 1;
				result.state = 1;
			}
			break;
		case 2:	/* 01 */
			if (byte & 0x1) {
				result.state = 3;
			} else {
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 2;
				result.state = 1;
			}
			break;
		case 3:	/* 011 */
			if (byte & 0x1) {
				result.state = 4;
			} else {
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 3;
				result.state = 1;
			}
			break;
		case 4:	/* 0111 */
			if (byte & 0x1) {
				result.state = 5;
			} else {
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 4;
				result.state = 1;
			}
			break;
		case 5:	/* 01111 */
			if (byte & 0x1) {
				result.state = 6;
			} else {
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 5;
				result.state = 1;
			}
			break;
		case 6:	/* 011111 */
			if (byte & 0x1) {
				result.state = 7;
			} else {
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 6;
				result.state = 0;
			}
			break;
		case 7:	/* 0111111 */
			if (byte & 0x1) {
				result.sync = 0;
				result.flag = 0;
				result.hunt = 1;
				result.state = 12;
			} else {
				result.sync = 0;
				result.flag = 1;
				result.hunt = 0;
				result.idle = 0;
				result.state = 0;
			}
			break;
		case 8:	/* 1 */
			if (byte & 0x1) {
				result.state = 9;
			} else {
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 1;
				result.state = 1;
			}
			break;
		case 9:	/* 11 */
			if (byte & 0x1) {
				result.state = 10;
			} else {
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 2;
				result.state = 1;
			}
			break;
		case 10:	/* 111 */
			if (byte & 0x1) {
				result.state = 11;
			} else {
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 3;
				result.state = 1;
			}
			break;
		case 11:	/* 1111 */
			if (byte & 0x1) {
				result.state = 12;
			} else {
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 4;
				result.state = 1;
			}
			break;
		case 12:	/* 11111 */
			if (byte & 0x1) {
				result.state = 13;
			} else {
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 5;
				result.state = 0;
			}
			break;
		case 13:	/* 111111 */
			if (byte & 0x1) {
				result.hunt = 1;
				result.sync = 0;
				result.idle = 1;
				result.flag = 0;
				result.state = 12;
			} else {
				result.sync = 0;
				result.hunt = 0;
				result.idle = 0;
				result.flag = 1;
				result.state = 0;
			}
			break;
		}
		byte >>= 1;
	}
	return result;
}
STATIC rx_entry_t rx_table_value7(int state, uint8_t byte)
{
	return rx_table_valueN(state, byte, 7);
}
STATIC rx_entry_t rx_table_value8(int state, uint8_t byte)
{
	return rx_table_valueN(state, byte, 8);
}

/*
 *  TX (Transmit) Table:
 *  -----------------------------------
 *  There is one TX table for 8-bit (octet) output values.  The table has 256
 *  entries and is used to perform, for one sample, zero insertion on frame
 *  bits for the transmitted bitstream.  It is the reponsiblity of the SDL
 *  driver to perform 8-bit to 7-bit conversion to DS0A.
 */
STATIC void tx_table_generate(void)
{
	int j, k;
	for (j = 0; j < HDLC_TX_STATES; j++)
		for (k = 0; k < 256; k++)
			*tx_index8(j, k) = tx_table_value(j, k);
}

/*
 *  RX (Received) Tables:
 *  -----------------------------------
 *  There is one RX table for 8 bit (octet) values.  The table has 256 entries
 *  and is used to perform, for one sample, zero deletion, abort detection,
 *  flag detection and residue calculation on the received bitstream.  The SDL
 *  driver is responsible for performing 7-bit to 8-bit conversion before
 *  delivering bits to the HDLC.
 */
STATIC void rx_table_generate7(void)
{
	int j, k;
	for (j = 0; j < HDLC_RX_STATES; j++)
		for (k = 0; k < 256; k++)
			*rx_index7(j, k) = rx_table_value7(j, k);
}
STATIC void rx_table_generate8(void)
{
	int j, k;
	for (j = 0; j < HDLC_RX_STATES; j++)
		for (k = 0; k < 256; k++)
			*rx_index8(j, k) = rx_table_value8(j, k);
}

/*
 *  BC (CRC) Tables:
 *  -----------------------------------
 *  CRC table organization:  This is a CRC table which contains lookups for
 *  all bit lengths less than or equal to 8.  There are 512 entries.  The
 *  first 256 entries are for 8-bit bit lengths, the next 128 entries are for
 *  7-bit bit lengths, the next 64 entries for 6-bit bit lengths, etc.
 */
STATIC void bc_table_generate(void)
{
	int pos = 0, bit_string, bit_length = 8, bit_mask = 0x100;
	do {
		for (bit_string = 0; bit_string < bit_mask; bit_string++, pos++)
			bc_table[pos] = bc_table_value(bit_string, bit_length);
		bit_length--;
	} while ((bit_mask >>= 1));
}

/*
 *  Table allocation
 *  -------------------------------------------------------------------------
 */
STATIC void hdlc_free_tables(void)
{
	if (bc_table) {
		free_pages((unsigned long) xchg(&bc_table, NULL), xchg(&bc_order, 0));
		printd(("cd: freed BC table kernel pages\n"));
	}
	if (tx_table) {
		free_pages((unsigned long) xchg(&tx_table, NULL), xchg(&tx_order, 0));
		printd(("cd: freed Tx table kernel pages\n"));
	}
	if (rx_table) {
		free_pages((unsigned long) xchg(&rx_table, NULL), xchg(&rx_order, 0));
		printd(("cd: freed Rx table kernel pages\n"));
	}
}
STATIC int hdlc_init_tables(void)
{
	size_t length;
	if (!bc_table) {
		length = HDLC_CRC_TABLE_LENGTH * sizeof(bc_entry_t);
		for (bc_order = 0; PAGE_SIZE << bc_order < length; bc_order++) ;
		if (!(bc_table = (bc_entry_t *) __get_free_pages(GFP_KERNEL, bc_order)))
			goto enomem;
		printd(("cd: allocated BC table size %u kernel pages\n", 1 << bc_order));
		bc_table_generate();
		printd(("cd: generated BC table\n"));
	}
	if (!tx_table) {
		length = HDLC_TX_TABLE_LENGTH * sizeof(tx_entry_t);
		for (tx_order = 0; PAGE_SIZE << tx_order < length; tx_order++) ;
		if (!(tx_table = (tx_entry_t *) __get_free_pages(GFP_KERNEL, tx_order)))
			goto enomem;
		printd(("cd: allocated Tx table size %u kernel pages\n", 1 << tx_order));
		tx_table_generate();
		printd(("cd: generated 8-bit Tx table\n"));
	}
	if (!rx_table) {
		length = 2 * (HDLC_RX_TABLE_LENGTH * sizeof(rx_entry_t));
		for (rx_order = 0; PAGE_SIZE << rx_order < length; rx_order++) ;
		if (!(rx_table = (rx_entry_t *) __get_free_pages(GFP_KERNEL, rx_order)))
			goto enomem;
		printd(("cd: allocated Rx table size %u kernel pages\n", 1 << rx_order));
		rx_table_generate8();
		printd(("cd: generated 8-bit Rx table\n"));
		rx_table7 = (rx_entry_t *) (((uint8_t *) rx_table) + (PAGE_SIZE << (rx_order - 1)));
		rx_table_generate7();
		printd(("cd: generated 7-bit Rx table\n"));
	}
	return (0);
      enomem:
	hdlc_free_tables();
	return (-ENOMEM);
}

/*
 *  ========================================================================
 *
 *  EVENTS
 *
 *  ========================================================================
 */

/*
 *  WAKEUP
 *  -----------------------------------
 *  This is called before the queue service routine unlocks the queue whenever
 *  we have received or transmitted message blocks.  cd_tx_block will pull
 *  data from the queue as necessary.  Thus the module takes its transmit data
 *  timing from its receive data timing.
 */
STATIC void cd_wakeup(queue_t *q)
{
	struct cd *cd = PRIV(q);
	if (!cd_tst_state(cd, CDF_ENABLED | CDF_OUTPUT_ACTIVE | CDF_READ_ACTIVE))
		return;
	if (ch_get_state(cd) != CHS_CONNECTED)
		return;
	cd_tx_block(q, cd);
	return;
}

/*
 *  -------------------------------------------------------------------------
 *
 *  CD User -> CD Provider Primitives
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  M_DATA
 *  -----------------------------------
 */
STATIC int cd_write(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	if (!cd_tst_state(cd, CDF_ENABLED | CDF_OUTPUT_ACTIVE | CDF_READ_ACTIVE))
		goto discard;
	if (ch_get_state(cd) != CHS_CONNECTED)
		goto discard;
	/* let cd_wakeup pull from the queue */
	return (-EAGAIN);
      discard:
	return (QR_DONE);	/* silent discard */
}

/*
 *  CD_INFO_REQ		0x00 - Information request
 *  -----------------------------------------------------------
 */
STATIC int cd_info_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_info_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	return cd_info_ack(q, cd);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_ATTACH_REQ	0x02 - Attach a PPA
 *  -----------------------------------------------------------
 */
STATIC int cd_attach_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_attach_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (cd_get_state(cd) != CD_UNATTACHED)
		goto outstate;
	if (cd->info.cd.cd_ppa_style != CD_STYLE2)
		goto notsupp;
	cd->ppa = p->cd_ppa;
	/* issue attach request to channel and wait for response */
	return ch_attach_req(q, cd, sizeof(cd->ppa), (caddr_t) &cd->ppa, 0);
      notsupp:
	return cd_error_ack(q, cd, p->cd_primitive, CD_NOTSUPP, EOPNOTSUPP);
      outstate:
	return cd_error_ack(q, cd, p->cd_primitive, CD_OUTSTATE, EPROTO);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, EMSGSIZE);
}

/*
 *  CD_DETACH_REQ	0x03 - Detach a PPA
 *  -----------------------------------------------------------
 */
STATIC int cd_detach_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_detach_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (cd_get_state(cd) != CD_DISABLED)
		goto outstate;
	/* issue detach request to channel and wait for response */
	return ch_detach_req(q, cd);
      outstate:
	return cd_error_ack(q, cd, p->cd_primitive, CD_OUTSTATE, 0);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_ENABLE_REQ	0x04 - Prepare a device
 *  -----------------------------------------------------------
 */
STATIC int cd_enable_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_enable_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (cd_get_state(cd) != CD_DISABLED)
		goto outstate;
	/* issue enable request to channel and wait for response */
	return ch_enable_req(q, cd);
      outstate:
	return cd_error_ack(q, cd, p->cd_primitive, CD_OUTSTATE, 0);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_DISABLE_REQ	0x05 - Disable a device
 *  -----------------------------------------------------------
 */
STATIC int cd_disable_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_disable_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (cd_get_state(cd) != CD_ENABLED)
		goto outstate;
	/* issue disconnect request to channel and wait for response */
	return ch_disconnect_req(q, cd, CHF_BOTH_DIR);
      outstate:
	return cd_error_ack(q, cd, p->cd_primitive, CD_OUTSTATE, 0);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_ALLOW_INPUT_REQ	0x0b - Allow input
 *  -----------------------------------------------------------
 */
STATIC int cd_allow_input_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_allow_input_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	switch (cd_get_state(cd)) {
	case CD_READ_ACTIVE:
	case CD_OUTPUT_ACTIVE:
	case CD_ENABLED:
	case CD_INPUT_ALLOWED:
		break;
	default:
		goto outstate;
	}
	return cd_ok_ack(q, cd, p->cd_primitive);
      outstate:
	return cd_error_ack(q, cd, p->cd_primitive, CD_OUTSTATE, 0);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_READ_REQ		0x0c - Wait-for-input request
 *  -----------------------------------------------------------
 */
STATIC int cd_read_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_read_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (cd_get_state(cd) != CD_ENABLED)
		goto outstate;
	return cd_error_ack(q, cd, p->cd_primitive, CD_NOTSUPP, 0);
      outstate:
	return cd_error_ack(q, cd, p->cd_primitive, CD_OUTSTATE, 0);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_UNITDATA_REQ	0x0d - Data send request
 *  -----------------------------------------------------------
 *  We strip off the CD_UNITDATA_REQ header and place it back on the queue as
 *  just M_DATA.  The M_DATA and outstanding CD_UNITDATA_REQ message blocks
 *  will be pulled off of the queue by cd_tx_block().
 */
STATIC int cd_unitdata_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_unitdata_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->cd_addr_type != CD_IMPLICIT)
		goto badaddrtype;
	if (p->cd_dest_addr_length != 0)
		goto badaddress;
	if (!cd_tst_state(cd, CDF_ENABLED | CDF_OUTPUT_ACTIVE | CDF_READ_ACTIVE))
		goto outstate;
	if (ch_get_state(cd) != CHS_CONNECTED)
		goto outstate;
	return (QR_STRIP);	/* strip down to data only */
      badaddress:
	return cd_error_ack(q, cd, p->cd_primitive, CD_BADADDRESS, 0);
      badaddrtype:
	return cd_error_ack(q, cd, p->cd_primitive, CD_BADADDRTYPE, 0);
      outstate:
	return cd_error_ack(q, cd, p->cd_primitive, CD_OUTSTATE, 0);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_WRITE_READ_REQ	0x0e - Write/read request
 *  -----------------------------------------------------------
 */
STATIC int cd_write_read_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_write_read_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (cd_get_state(cd) != CD_ENABLED)
		goto outstate;
	return cd_error_ack(q, cd, p->cd_primitive, CD_NOTSUPP, 0);
      outstate:
	return cd_error_ack(q, cd, p->cd_primitive, CD_OUTSTATE, 0);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_HALT_INPUT_REQ	0x11 - Halt input
 *  -----------------------------------------------------------
 */
STATIC int cd_halt_input_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_halt_input_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (cd_get_state(cd) != CD_INPUT_ALLOWED)
		goto outstate;
	return cd_error_ack(q, cd, p->cd_primitive, CD_NOTSUPP, 0);
      outstate:
	return cd_error_ack(q, cd, p->cd_primitive, CD_OUTSTATE, 0);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_ABORT_OUTPUT_REQ	0x12 - Abort output
 *  -----------------------------------------------------------
 */
STATIC int cd_abort_output_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_abort_output_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (cd_get_state(cd) != CD_OUTPUT_ACTIVE)
		goto outstate;
	return cd_error_ack(q, cd, p->cd_primitive, CD_NOTSUPP, 0);
      outstate:
	return cd_error_ack(q, cd, p->cd_primitive, CD_OUTSTATE, 0);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_MUX_NAME_REQ	0x13 - get mux name (Gcom)
 *  -----------------------------------------------------------
 */
STATIC int cd_mux_name_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
#if 0
	cd_mux_name_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	return cd_error_ack(q, cd, p->cd_primitive, CD_NOTSUPP, 0);
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
#endif
	return cd_error_ack(q, cd, *(ulong *) mp->b_rptr, CD_NOTSUPP, 0);
}

/*
 *  CD_MODEM_SIG_REQ	0x15 - Assert modem signals (Gcom)
 *  -----------------------------------------------------------
 */
STATIC int cd_modem_sig_req(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_modem_sig_req_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
#if 0
	return cd_ok_ack(q, cd, p->cd_primitive);
#else
	return cd_error_ack(q, cd, p->cd_primitive, CD_NOTSUPP, 0);
#endif
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  CD_MODEM_SIG_POLL	0x17 - requests a CD_MODEM_SIG_IND (Gcom)
 *  -----------------------------------------------------------
 */
STATIC int cd_modem_sig_poll(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	cd_modem_sig_poll_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
#if 0
	return cd_modem_sig_ind(q, cd, 0);
#else
	return cd_error_ack(q, cd, p->cd_primitive, CD_NOTSUPP, 0);
#endif
      badprim:
	return cd_error_ack(q, cd, p->cd_primitive, CD_PROTOSHORT, 0);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  CH-Provider -> CH-User Primitives
 *
 *  -------------------------------------------------------------------------
 */

/*
 *  M_DATA
 *  -----------------------------------
 */
STATIC int ch_read(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	if (!cd_tst_state(cd, CDF_INPUT_ALLOWED | CDF_READ_ACTIVE))
		goto discard;
	if (ch_get_state(cd) != CHS_CONNECTED)
		goto discard;
	return cd_rx_block(q, cd, mp);
      discard:
	return (QR_DONE);
}

/*
 *  CH_INFO_ACK
 *  -----------------------------------
 */
STATIC int ch_info_ack(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_info_ack *p = (typeof(p)) mp->b_rptr;
	ulong cp_type;
	ulong oldstate = ch_get_state(cd);
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	cd->info.ch = *p;
	if (ch_get_state(cd) == CHS_UNINIT) {
		switch (p->ch_state) {
		case CHS_DETACHED:
			cd->info.cd.cd_ppa_style = cd->i_style = CD_STYLE2;
			break;
		case CHS_ATTACHED:
			/* treat already attached channels as CD_STYLE1 */
			cd->info.cd.cd_ppa_style = cd->i_style = CD_STYLE1;
			break;
		default:
			goto eio;
		}
		ch_set_state(cd, p->ch_state);
	}
	if (ch_get_state(cd) == CHS_ATTACHED) {
		if (!p->ch_parm_length)
			goto eio;
		if (mp->b_wptr > mp->b_rptr + p->ch_parm_offset + p->ch_parm_length)
			goto eio;
		if (p->ch_parm_length < sizeof(cp_type))
			goto eio;
		bcopy(mp->b_rptr + p->ch_parm_offset, &cp_type, sizeof(cp_type));
		if (cp_type != CH_PARMS_CIRCUIT)
			goto eio;
		if (p->ch_parm_length < sizeof(cd->parm))
			goto eio;
		bcopy(mp->b_rptr + p->ch_parm_offset, &cd->parm, sizeof(cd->parm));
		/* check circuit parameters */
		if (cd->parm.cp_block_size == 0 || cd->parm.cp_block_size > 2048)
			goto eio;
		if (cd->parm.cp_encoding != CH_ENCODING_NONE)
			goto eio;
		if (cd->parm.cp_sample_size != 7 && cd->parm.cp_sample_size != 8)
			goto eio;
		if (cd->parm.cp_tx_channels != 1 || cd->parm.cp_rx_channels != 1)
			goto eio;
	}
	if (oldstate == CHS_ATTACHED)
		return cd_ok_ack(q, cd, CD_ATTACH_REQ);
	return (QR_DONE);
      eio:
	switch (oldstate) {
	case CHS_UNINIT:
		ch_set_state(cd, CHS_UNUSABLE);
		return (-EIO);
	case CHS_ATTACHED:
		return cd_error_ack(q, cd, CD_ATTACH_REQ, CD_FATALERR, EIO);
	}
	swerr();
	return (-EIO);
}

/*
 *  CH_OPTMGMT_ACK
 *  -----------------------------------
 */
STATIC int ch_optmgmt_ack(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_optmgmt_ack *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	return (QR_DONE);
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

/*
 *  CH_OK_ACK
 *  -----------------------------------
 */
STATIC int ch_ok_ack(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_ok_ack *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	switch (ch_get_state(cd)) {
	case CHS_WACK_AREQ:
		ch_set_state(cd, CHS_ATTACHED);
		/* request information concerning attached circuit */
		return ch_info_req(q, cd);
	case CHS_WACK_UREQ:
		ch_set_state(cd, CHS_DETACHED);
		return cd_ok_ack(q, cd, CD_DETACH_REQ);
	case CHS_WACK_EREQ:
		ch_set_state(cd, CHS_WCON_EREQ);
		return (QR_DONE);
	case CHS_WACK_RREQ:
		ch_set_state(cd, CHS_WCON_RREQ);
		return (QR_DONE);
	case CHS_WACK_CREQ:
		ch_set_state(cd, CHS_WCON_CREQ);
		return (QR_DONE);
	case CHS_WACK_DREQ:
		ch_set_state(cd, CHS_WCON_DREQ);
		return (QR_DONE);
	}
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

/*
 *  CH_ERROR_ACK
 *  -----------------------------------
 */
STATIC int ch_error_ack(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_error_ack *p = (typeof(p)) mp->b_rptr;
	ulong error = CD_EVENT, reason = 0;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	switch (p->ch_error_type) {
	case CHSYSERR:
		error = CD_SYSERR;
		reason = p->ch_unix_error;
		break;
	case CHBADADDR:
		error = CD_BADADDRESS;
		break;
	case CHOUTSTATE:
		error = CD_OUTSTATE;
		break;
	case CHBADFLAG:
		error = CD_BADPRIM;
		break;
	case CHBADPRIM:
		error = CD_PROTOSHORT;
		break;
	case CHNOTSUPP:
		error = CD_NOTSUPP;
		break;
	default:
	case CHBADOPT:
	case CHBADPARM:
	case CHBADPARMTYPE:
		error = CD_EVENT;
		reason = p->ch_error_type;
		break;
	}
	switch (ch_get_state(cd)) {
	case CHS_WACK_AREQ:
		ch_set_state(cd, CHS_DETACHED);
		return cd_error_ack(q, cd, CD_ATTACH_REQ, CD_EVENT, 0);
	case CHS_WACK_UREQ:
		ch_set_state(cd, CHS_ATTACHED);
		return cd_error_ack(q, cd, CD_DETACH_REQ, CD_EVENT, 0);
	case CHS_WACK_EREQ:
		ch_set_state(cd, CHS_ATTACHED);
		return cd_error_ack(q, cd, CD_ENABLE_REQ, CD_EVENT, 0);
	case CHS_WACK_CREQ:
		return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
	case CHS_WACK_DREQ:
		ch_set_state(cd, CHS_CONNECTED);
		return cd_error_ack(q, cd, CD_DISABLE_REQ, CD_EVENT, 0);
	case CHS_WACK_RREQ:
		return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
	}
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

/*
 *  CH_ENABLE_CON
 *  -----------------------------------
 */
STATIC int ch_enable_con(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_enable_con *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	if (ch_get_state(cd) != CHS_WCON_EREQ)
		goto eio;
	/* issue connect request to channel and wait for response */
	return ch_connect_req(q, cd, CHF_BOTH_DIR);
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

/*
 *  CH_CONNECT_CON
 *  -----------------------------------
 */
STATIC int ch_connect_con(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_connect_con *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	if (ch_get_state(cd) != CHS_WCON_CREQ)
		goto eio;
	ch_set_state(cd, CHS_CONNECTED);
	return cd_enable_con(q, cd);
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

/*
 *  CH_DATA_IND
 *  -----------------------------------
 */
STATIC int ch_data_ind(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_data_ind *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	if (!mp->b_cont)
		goto eio;
	return (QR_STRIP);
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

/*
 *  CH_DISCONNECT_IND
 *  -----------------------------------
 */
STATIC int ch_disconnect_ind(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_disconnect_ind *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	if (ch_get_state(cd) != CHS_CONNECTED)
		goto eio;
	cd->flags |= HDLC_F_DISCONNECTED;
	return ch_disable_req(q, cd);
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

/*
 *  CH_DISCONNECT_CON
 *  -----------------------------------
 */
STATIC int ch_disconnect_con(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_disconnect_con *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	if (ch_get_state(cd) != CHS_WCON_DREQ)
		goto eio;
	/* issue disable request to channel and wait for response */
	return ch_disable_req(q, cd);
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

/*
 *  CH_DISABLE_IND
 *  -----------------------------------
 */
STATIC int ch_disable_ind(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_disable_ind *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	if (ch_get_state(cd) != CHS_ENABLED)
		goto eio;
	ch_set_state(cd, CHS_ATTACHED);
	return cd_error_ind(q, cd, CD_DISC, 0, CD_DISABLED, NULL);
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

/*
 *  CH_DISABLE_CON
 *  -----------------------------------
 */
STATIC int ch_disable_con(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_disable_con *p = (typeof(p)) mp->b_rptr;
	int rtn;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	if (ch_get_state(cd) != CHS_WCON_RREQ)
		goto eio;
	if (cd->flags & HDLC_F_DISCONNECTED)
		rtn = cd_error_ind(q, cd, CD_DISC, 0, CD_DISABLED, NULL);
	else
		rtn = cd_disable_con(q, cd);
	if (rtn >= 0) {
		ch_set_state(cd, CHS_ATTACHED);
		cd->flags &= ~HDLC_F_DISCONNECTED;
	}
	return (rtn);
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

#ifndef CD_YEL
#define CD_YEL (CD_RI << 1)
#define CD_BLU (CD_RI << 2)
#define CD_RED (CD_RI << 3)
#endif

/*
 *  CH_EVENT_IND
 *  -----------------------------------
 */
STATIC int ch_event_ind(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	struct CH_event_ind *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr > mp->b_rptr + sizeof(*p))
		goto eio;
	switch (p->ch_event) {
	case CH_EVT_DCD_ASSERT:
		cd->sigs |= CD_DCD;
		break;
	case CH_EVT_DCD_DEASSERT:
		cd->sigs &= ~CD_DCD;
		break;
	case CH_EVT_DSR_ASSERT:
		cd->sigs |= CD_DSR;
		break;
	case CH_EVT_DSR_DEASSERT:
		cd->sigs &= ~CD_DSR;
		break;
	case CH_EVT_DTR_ASSERT:
		cd->sigs |= CD_DTR;
		break;
	case CH_EVT_DTR_DEASSERT:
		cd->sigs &= ~CD_DTR;
		break;
	case CH_EVT_RTS_ASSERT:
		cd->sigs |= CD_RTS;
		break;
	case CH_EVT_RTS_DEASSERT:
		cd->sigs &= ~CD_RTS;
		break;
	case CH_EVT_CTS_ASSERT:
		cd->sigs |= CD_CTS;
		break;
	case CH_EVT_CTS_DEASSERT:
		cd->sigs &= ~CD_CTS;
		break;
	case CH_EVT_RI_ASSERT:
		cd->sigs |= CD_RI;
		break;
	case CH_EVT_RI_DEASSERT:
		cd->sigs &= ~CD_RI;
		break;
	case CH_EVT_YEL_ALARM:
		cd->sigs |= CD_YEL;
		break;
	case CH_EVT_BLU_ALARM:
		cd->sigs |= CD_BLU;
		break;
	case CH_EVT_RED_ALARM:
		cd->sigs |= CD_RED;
		break;
	case CH_EVT_NO_ALARM:
		cd->sigs &= ~(CD_YEL | CD_BLU | CD_RED);
		break;
	default:
		goto discard;
	}
	return cd_modem_sig_ind(q, cd, cd->sigs);
      discard:
	swerr();
	return (-EIO);		/* ignore */
      eio:
	swerr();
	return cd_error_ind(q, cd, CD_FATALERR, 0, CD_UNUSABLE, NULL);
}

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

/*
 *  -------------------------------------------------------------------------
 *
 *  HDLC IO Controls
 *
 *  -------------------------------------------------------------------------
 */
STATIC int cd_test_config(struct cd *cd, hdlc_config_t * arg)
{
	int ret = 0;
	int flags = 0;
	lis_spin_lock_irqsave(&cd->lock, &flags);
	do {
#if 0
		if (!arg->t8)
			arg->t8 = cd->config.t8;
		if (!arg->Tin)
			arg->Tin = cd->config.Tin;
		if (!arg->Tie)
			arg->Tie = cd->config.Tie;
		if (!arg->T)
			arg->T = cd->config.T;
		if (!arg->D)
			arg->D = cd->config.D;
		if (!arg->Te)
			arg->Te = cd->config.Te;
		if (!arg->De)
			arg->De = cd->config.De;
		if (!arg->Ue)
			arg->Ue = cd->config.Ue;
		if (!arg->N)
			arg->N = cd->config.N;
#endif
		if (!arg->m)
			arg->m = cd->config.m;
		if (!arg->b)
			arg->b = cd->config.b;
		else if (arg->b != cd->config.b) {
			ret = -EINVAL;
			break;
		}
	} while (0);
	lis_spin_unlock_irqrestore(&cd->lock, &flags);
	return (ret);
}
STATIC int cd_commit_config(struct cd *cd, hdlc_config_t * arg)
{
	int flags = 0;
	lis_spin_lock_irqsave(&cd->lock, &flags);
	{
		cd_test_config(cd, arg);
		cd->config = *arg;
	}
	lis_spin_unlock_irqrestore(&cd->lock, &flags);
	return (0);
}
STATIC int cd_iocgoptions(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			*arg = cd->option;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_iocsoptions(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			cd->option = *arg;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_iocgconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		hdlc_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			*arg = cd->config;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_iocsconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		hdlc_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			cd->config = *arg;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_ioctconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		hdlc_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		return cd_test_config(cd, arg);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_ioccconfig(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		hdlc_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		return cd_commit_config(cd, arg);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_iocgstatem(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		hdlc_statem_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			*arg = cd->statem;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_ioccmreset(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	(void) cd;
	(void) mp;
	fixme(("%s: Master reset\n", HDLC_MOD_NAME));
	return (-EOPNOTSUPP);
}
STATIC int cd_iocgstatsp(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		hdlc_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			*arg = cd->statsp;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_iocsstatsp(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		hdlc_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			cd->statsp = *arg;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_iocgstats(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		hdlc_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			*arg = cd->stats;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_ioccstats(queue_t *q, mblk_t *mp)
{
	int flags = 0;
	struct cd *cd = PRIV(q);
	(void) mp;
	lis_spin_lock_irqsave(&cd->lock, &flags);
	{
		bzero(&cd->stats, sizeof(cd->stats));
	}
	lis_spin_unlock_irqrestore(&cd->lock, &flags);
	return (0);
}
STATIC int cd_iocgnotify(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		hdlc_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			*arg = cd->notify;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_iocsnotify(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		hdlc_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			cd->notify = *arg;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_ioccnotify(queue_t *q, mblk_t *mp)
{
	if (mp->b_cont) {
		struct cd *cd = PRIV(q);
		int flags = 0;
		hdlc_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr;
		lis_spin_lock_irqsave(&cd->lock, &flags);
		{
			cd->notify.events &= ~arg->events;
		}
		lis_spin_unlock_irqrestore(&cd->lock, &flags);
		return (0);
	}
	rare();
	return (-EINVAL);
}
STATIC int cd_ioccmgmt(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	int ret, flags = 0;
	(void) mp;
	lis_spin_lock_irqsave(&cd->lock, &flags);
	{
		ret = -EOPNOTSUPP;
	}
	lis_spin_unlock_irqrestore(&cd->lock, &flags);
	return (ret);
}

/*
 *  ========================================================================
 *
 *  STREAMS Message Handling
 *
 *  ========================================================================
 *
 *  M_IOCTL Handling
 *  -----------------------------------------------------------------------
 */
STATIC int cd_w_ioctl(queue_t *q, mblk_t *mp)
{
	struct cd *cd = 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", HDLC_MOD_NAME, cd, nr));
			ret = (-EINVAL);
			break;
		default:
			ptrace(("%s: %p: ERROR: Unsupported STREAMS ioctl %d\n", HDLC_MOD_NAME, cd, nr));
			ret = (-EOPNOTSUPP);
			break;
		}
		break;
	}
	case LMI_IOC_MAGIC:
	{
		if (count < size || cd_get_state(cd) == CD_UNUSABLE) {
			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 HDLC ioctl %d\n", HDLC_MOD_NAME, cd, nr));
			ret = -EOPNOTSUPP;
			break;
		}
		break;
	}
	case HDLC_IOC_MAGIC:
	{
		if (count < size || cd_get_state(cd) == CD_UNUSABLE) {
			ret = (-EINVAL);
			break;
		}
		switch (nr) {
		case _IOC_NR(HDLC_IOCGOPTIONS):	/* lmi_option_t */
			ret = cd_iocgoptions(q, mp);
			break;
		case _IOC_NR(HDLC_IOCSOPTIONS):	/* lmi_option_t */
			ret = cd_iocsoptions(q, mp);
			break;
		case _IOC_NR(HDLC_IOCGCONFIG):	/* hdlc_config_t */
			ret = cd_iocgconfig(q, mp);
			break;
		case _IOC_NR(HDLC_IOCSCONFIG):	/* hdlc_config_t */
			ret = cd_iocsconfig(q, mp);
			break;
		case _IOC_NR(HDLC_IOCTCONFIG):	/* hdlc_config_t */
			ret = cd_ioctconfig(q, mp);
			break;
		case _IOC_NR(HDLC_IOCCCONFIG):	/* hdlc_config_t */
			ret = cd_ioccconfig(q, mp);
			break;
		case _IOC_NR(HDLC_IOCGSTATEM):	/* hdlc_statem_t */
			ret = cd_iocgstatem(q, mp);
			break;
		case _IOC_NR(HDLC_IOCCMRESET):	/* hdlc_statem_t */
			ret = cd_ioccmreset(q, mp);
			break;
		case _IOC_NR(HDLC_IOCGSTATSP):	/* lmi_sta_t */
			ret = cd_iocgstatsp(q, mp);
			break;
		case _IOC_NR(HDLC_IOCSSTATSP):	/* lmi_sta_t */
			ret = cd_iocsstatsp(q, mp);
			break;
		case _IOC_NR(HDLC_IOCGSTATS):	/* hdlc_stats_t */
			ret = cd_iocgstats(q, mp);
			break;
		case _IOC_NR(HDLC_IOCCSTATS):	/* hdlc_stats_t */
			ret = cd_ioccstats(q, mp);
			break;
		case _IOC_NR(HDLC_IOCGNOTIFY):	/* hdlc_notify_t */
			ret = cd_iocgnotify(q, mp);
			break;
		case _IOC_NR(HDLC_IOCSNOTIFY):	/* hdlc_notify_t */
			ret = cd_iocsnotify(q, mp);
			break;
		case _IOC_NR(HDLC_IOCCNOTIFY):	/* hdlc_notify_t */
			ret = cd_ioccnotify(q, mp);
			break;
		case _IOC_NR(HDLC_IOCCMGMT):	/* */
			ret = cd_ioccmgmt(q, mp);
			break;
		default:
			ptrace(("%s: ERROR: Unsupported HDLC ioctl %d\n", HDLC_MOD_NAME, nr));
			ret = -EOPNOTSUPP;
			break;
		}
		break;
	}
	default:
		ret = (QR_PASSALONG);
		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 HDLC.
 *  -----------------------------------
 */
STATIC int cd_w_proto(queue_t *q, mblk_t *mp)
{
	int rtn;
	ulong prim;
	struct cd *cd = PRIV(q);
	cd->i_oldstate = cd_get_state(cd);
	if ((prim = *(ulong *) mp->b_rptr) == CD_UNITDATA_REQ) {
		printd(("%s: %p: -> CD_UNITDATA_REQ [%d]\n", HDLC_MOD_NAME, cd, msgdsize(mp->b_cont)));
		if ((rtn = cd_unitdata_req(q, mp)) < 0)
			cd_set_state(cd, cd->i_oldstate);
		return (rtn);
	}
	switch (prim) {
	case CD_INFO_REQ:
		printd(("%s: %p: -> CD_INFO_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_info_req(q, mp);
		break;
	case CD_ATTACH_REQ:
		printd(("%s: %p: -> CD_ATTACH_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_attach_req(q, mp);
		break;
	case CD_DETACH_REQ:
		printd(("%s: %p: -> CD_DETACH_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_detach_req(q, mp);
		break;
	case CD_ENABLE_REQ:
		printd(("%s: %p: -> CD_ENABLE_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_enable_req(q, mp);
		break;
	case CD_DISABLE_REQ:
		printd(("%s: %p: -> CD_DISABLE_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_disable_req(q, mp);
		break;
	case CD_ALLOW_INPUT_REQ:
		printd(("%s: %p: -> CD_ALLOW_INPUT_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_allow_input_req(q, mp);
		break;
	case CD_READ_REQ:
		printd(("%s: %p: -> CD_READ_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_read_req(q, mp);
		break;
	case CD_UNITDATA_REQ:
		printd(("%s: %p: -> CD_UNITDATA_REQ [%d]\n", HDLC_MOD_NAME, cd, msgdsize(mp->b_cont)));
		rtn = cd_unitdata_req(q, mp);
		break;
	case CD_WRITE_READ_REQ:
		printd(("%s: %p: -> CD_WRITE_READ_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_write_read_req(q, mp);
		break;
	case CD_HALT_INPUT_REQ:
		printd(("%s: %p: -> CD_HALT_INPUT_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_halt_input_req(q, mp);
		break;
	case CD_ABORT_OUTPUT_REQ:
		printd(("%s: %p: -> CD_ABORT_OUTPUT_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_abort_output_req(q, mp);
		break;
	case CD_MUX_NAME_REQ:
		printd(("%s: %p: -> CD_MUX_NAME_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_mux_name_req(q, mp);
		break;
	case CD_MODEM_SIG_REQ:
		printd(("%s: %p: -> CD_MODEM_SIG_REQ\n", HDLC_MOD_NAME, cd));
		rtn = cd_modem_sig_req(q, mp);
		break;
	case CD_MODEM_SIG_POLL:
		printd(("%s: %p: -> CD_MODEM_SIG_POLL\n", HDLC_MOD_NAME, cd));
		rtn = cd_modem_sig_poll(q, mp);
		break;
	default:
		printd(("%s: %p -> CD_????\n", HDLC_MOD_NAME, cd));
		rtn = cd_error_ack(q, cd, prim, CD_BADPRIM, 0);
		break;
	}
	if (rtn < 0)
		cd_set_state(cd, cd->i_oldstate);
	return (rtn);
}

/*
 *  Primitives from SDL to HDLC.
 *  -----------------------------------
 */
STATIC int cd_r_proto(queue_t *q, mblk_t *mp)
{
	int rtn;
	ulong prim;
	struct cd *cd = PRIV(q);
	ulong oldstate = ch_get_state(cd);
	/* Fast Path */
	if ((prim = *(ulong *) mp->b_rptr) == CH_DATA_IND) {
		printd(("%s: %p: CH_DATA_IND [%d] <-\n", HDLC_MOD_NAME, cd, msgdsize(mp->b_cont)));
		if ((rtn = ch_data_ind(q, mp)) < 0)
			ch_set_state(cd, oldstate);
		return (rtn);
	}
	switch (prim) {
	case CH_INFO_ACK:
		printd(("%s: %p: CH_INFO_ACK\n", HDLC_MOD_NAME, cd));
		rtn = ch_info_ack(q, mp);
		break;
	case CH_OPTMGMT_ACK:
		printd(("%s: %p: CH_OPTMGMT_ACK\n", HDLC_MOD_NAME, cd));
		rtn = ch_optmgmt_ack(q, mp);
		break;
	case CH_OK_ACK:
		printd(("%s: %p: CH_OK_ACK\n", HDLC_MOD_NAME, cd));
		rtn = ch_ok_ack(q, mp);
		break;
	case CH_ERROR_ACK:
		printd(("%s: %p: CH_ERROR_ACK\n", HDLC_MOD_NAME, cd));
		rtn = ch_error_ack(q, mp);
		break;
	case CH_ENABLE_CON:
		printd(("%s: %p: CH_ENABLE_CON\n", HDLC_MOD_NAME, cd));
		rtn = ch_enable_con(q, mp);
		break;
	case CH_CONNECT_CON:
		printd(("%s: %p: CH_CONNECT_CON\n", HDLC_MOD_NAME, cd));
		rtn = ch_connect_con(q, mp);
		break;
	case CH_DATA_IND:
		printd(("%s: %p: CH_DATA_IND\n", HDLC_MOD_NAME, cd));
		rtn = ch_data_ind(q, mp);
		break;
	case CH_DISCONNECT_IND:
		printd(("%s: %p: CH_DISCONNECT_IND\n", HDLC_MOD_NAME, cd));
		rtn = ch_disconnect_ind(q, mp);
		break;
	case CH_DISCONNECT_CON:
		printd(("%s: %p: CH_DISCONNECT_CON\n", HDLC_MOD_NAME, cd));
		rtn = ch_disconnect_con(q, mp);
		break;
	case CH_DISABLE_IND:
		printd(("%s: %p: CH_DISABLE_IND\n", HDLC_MOD_NAME, cd));
		rtn = ch_disable_ind(q, mp);
		break;
	case CH_DISABLE_CON:
		printd(("%s: %p: CH_DISABLE_CON\n", HDLC_MOD_NAME, cd));
		rtn = ch_disable_con(q, mp);
		break;
	case CH_EVENT_IND:
		printd(("%s: %p: CH_EVENT_IND\n", HDLC_MOD_NAME, cd));
		rtn = ch_event_ind(q, mp);
		break;
	default:
		/* dump anthing we don't recognize */
		swerr();
		rtn = (-EFAULT);
		break;
	}
	if (rtn < 0)
		ch_set_state(cd, oldstate);
	return (rtn);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int cd_w_data(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	(void) cd;
	printd(("%s: %p: -> M_DATA [%d]\n", HDLC_MOD_NAME, cd, msgdsize(mp)));
	return cd_write(q, mp);
}
STATIC INLINE int cd_r_data(queue_t *q, mblk_t *mp)
{
	struct cd *cd = PRIV(q);
	(void) cd;
	printd(("%s: %p: M_DATA [%d] <-\n", HDLC_MOD_NAME, cd, msgdsize(mp)));
	return ch_read(q, mp);
}

/*
 *  =========================================================================
 *
 *  PUT and SRV
 *
 *  =========================================================================
 */
STATIC int cd_r_prim(queue_t *q, mblk_t *mp)
{
	/* Fast Path */
	if (mp->b_datap->db_type == M_DATA)
		return cd_r_data(q, mp);
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return cd_r_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return cd_r_proto(q, mp);
	case M_FLUSH:
		return ss7_r_flush(q, mp);
	}
	return (QR_PASSFLOW);
}
STATIC int cd_w_prim(queue_t *q, mblk_t *mp)
{
	/* Fast Path */
	if (mp->b_datap->db_type == M_DATA)
		return cd_w_data(q, mp);
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return cd_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return cd_w_proto(q, mp);
	case M_FLUSH:
		return ss7_w_flush(q, mp);
	case M_IOCTL:
		return cd_w_ioctl(q, mp);
	}
	return (QR_PASSFLOW);
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 */
/*
 *  OPEN
 *  -------------------------------------------------------------------------
 */
STATIC struct cd *cd_list = NULL;
STATIC int cd_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 cd *cd;
		/* test for multiple push */
		for (cd = cd_list; cd; cd = cd->next) {
			if (cd->u.dev.cmajor == cmajor && cd->u.dev.cminor == cminor) {
				MOD_DEC_USE_COUNT;
				return (ENXIO);
			}
		}
		if (!(cd = hdlc_alloc_priv(q, &cd_list, devp, crp))) {
			MOD_DEC_USE_COUNT;
			return (ENOMEM);
		}
		/* generate immediate information request */
		ch_info_req(q, cd);
		return (0);
	}
	MOD_DEC_USE_COUNT;
	return (EIO);
}

/*
 *  CLOSE
 *  -------------------------------------------------------------------------
 */
STATIC int cd_close(queue_t *q, int flag, cred_t *crp)
{
	(void) flag;
	(void) crp;
	hdlc_free_priv(q);
	MOD_DEC_USE_COUNT;
	return (0);
}

/*
 *  =======================================================================
 *
 *  LiS Module Initialization (For unregistered driver.)
 *
 *  =======================================================================
 */
STATIC int hdlc_initialized = 0;
STATIC void hdlc_init(void)
{
	unless(hdlc_initialized > 0, return);
	cmn_err(CE_NOTE, HDLC_BANNER);	/* console splash */
	if ((hdlc_initialized = hdlc_init_caches()) || (hdlc_initialized = hdlc_init_tables())) {
		cmn_err(CE_PANIC, "%s: ERROR: could not allocate caches", HDLC_MOD_NAME);
	} else if ((hdlc_initialized = lis_register_strmod(&hdlc_info, HDLC_MOD_NAME)) < 0) {
		cmn_err(CE_WARN, "%s: couldn't register module", HDLC_MOD_NAME);
		hdlc_free_tables();
		hdlc_free_caches();
		return;
	}
	hdlc_initialized = 1;
	return;
}
STATIC void hdlc_terminate(void)
{
	ensure(hdlc_initialized > 0, return);
	if ((hdlc_initialized = lis_unregister_strmod(&hdlc_info)) < 0) {
		cmn_err(CE_PANIC, "%s: couldn't unregister module", HDLC_MOD_NAME);
	} else {
		hdlc_free_tables();
		hdlc_free_caches();
		hdlc_initialized = 0;
	}
	return;
}

/*
 *  =======================================================================
 *
 *  Kernel Module Initialization
 *
 *  =======================================================================
 */
int init_module(void)
{
	hdlc_init();
	if (hdlc_initialized < 0)
		return hdlc_initialized;
	return (0);
}
void cleanup_module(void)
{
	hdlc_terminate();
}


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

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

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