/*****************************************************************************
 * Copyright (c) 1988-96 Digi International Inc., All Rights Reserved
 * 
 * This  software  contains  proprietary  and  confidential   information   of
 * Digi International  Inc.   By  accepting  transfer  of this copy, Recipient
 * agrees to retain this software in  confidence,  to  prevent  disclosure  to
 * others,  and  to make no  use of this software other than that for which it
 * was delivered.  This is an unpublished copyright work  Digi   International
 * Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is
 * strictly prohibited.
 * 
 * Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc.
 * Confidential  when   combined   with   the   aggregated  modules  for  this
 * product) OBJECT CODE ONLY SOURCE MATERIALS
 * 
 * (C) COPYRIGHT Digi International Inc. 1995
 * 
 * All Rights Reserved
 * 
 * US   Government   Users   Restricted   Rights   -   Use,   duplication   or
 * disclosure  restricted by GSA ADP Schedule Contract with Digi International
 * Inc.
 * 
 *****************************************************************************/

/*
 * @(#) $Id: frf9.c,v 1.10 1997/10/21 20:48:06 gary Exp $
 */

/*
 * This module handles most activities and algorithms relating to
 * specification FRF.9 (Data Compression over Frame Relay) and
 * FRF.9 Annex A (LZS Data Compression Specifics).
 *
 * This module makes use of the lower layer "STAC" library.
 *
 * User-defined parameters within the FRF9_t structure, such as
 * process-mode and check-mode, are not defined here.  They must
 * be defined elsewhere in the Frame Relay application.
 */

#include "types.h"
#include "stream.h"
#include "fr_cont.h"
#include "frs.h"
#include "frsencap.h"

// Stac Prototypes
int			StacReleaseContext();
uint32		StacGetDecompressResults();
uint32		StacGetCompressResults();

/*
 * InitFRF9Session
 *
 * Purpose:  To initialize an FRF.9 session (i.e. initialize all state
 *           variables, and begin negotiations with the peer entity)
 *
 * Parameters:  pCCB -- A pointer to an CCB (aka DLCI) structure.
 *
 * Returns:   1 on success, 0 on failure.
 *
 * Notes:   NONE.
 */

uint32
InitFRF9Session(pCCB)
CCB *pCCB;
{

	printf("InitFRF9") ;
	/* Guard against multiple calls */
	if (!pCCB || pCCB->FRF9.Phase != FRF9_PHASE_UNUSED) return 0 ;

	printf("pCCB->FRF9.Phase = %x",pCCB->FRF9.Phase ); 
	/* Create timers; abort if unsuccessful */
	if ((pCCB->FRF9.ModeOneTimer = FRSTMR_create(pCCB->hCCB,
	FRF9LaunchModeOne, 0)) == 0xFFFF) return 0 ;

	printf("InitFRF9 - 2") ;
	if ((pCCB->FRF9.ResetTimer = FRSTMR_create(pCCB->hCCB,
	FRF9RRTransmit, 0)) == 0xFFFF) {
		FRSTMR_destroy(pCCB->FRF9.ModeOneTimer);
		return 0 ;
	}

	printf("InitFRF9 - 3") ;

	/* Initialize Tx Queue */
	FRSQT_flush((QTCB *)&pCCB->FRF9.TxQueue) ;
	pCCB->FRF9.TxQueue.qcnt = 0 ;

	printf("InitFRF9 - 4") ;
	/* Start in Disabled state */
	pCCB->FRF9.Phase = FRF9_PHASE_DISABLED ;

	printf("InitFRF9 - 5 RetryPeriod = %x", pCCB->FRF9.RetryPeriod) ;

	/* Send initial Mode-1 request */
	if (pCCB->FRF9.RetryPeriod)
	{
		printf("FRF9LaunchMode Set") ;
		FRF9LaunchModeOne(pCCB->hCCB) ;
	}

	return 1;
}

/*
 * StopFRF9Session
 *
 * Purpose:  To halt an FRF.9 session
 *
 * Parameters:  pCCB -- A pointer to a CCB (aka DLCI) structure.
 *
 * Returns:   1 always.
 *
 * Notes:   The Phase state is set to disabled.
 */

uint32
StopFRF9Session(pCCB)
CCB *pCCB;
{
	/* Guard against multiple/unnecessary calls */
	if (!pCCB || pCCB->FRF9.Phase == FRF9_PHASE_UNUSED) return 1 ;

	/* Kill timers */
	FRSTMR_destroy(pCCB->FRF9.ModeOneTimer);
	FRSTMR_destroy(pCCB->FRF9.ResetTimer);

	StopFRF9OperationPhase(pCCB);

	pCCB->FRF9.Phase = FRF9_PHASE_UNUSED ;

	return 1 ;
}

/*
 * StartFRF9OperationPhase
 *
 * Purpose:  To acquire and initialize data compression contexts.
 *
 * Parameters:  pCCB -- A pointer to a CCB (aka DLCI) structure.
 *
 * Returns:   1 on success; 0 on failure.
 *
 * Notes:   No action is taken if session is not in the operation phase.
 *          ContextCount will dictate the number of contexts allocated.
 */

uint32
StartFRF9OperationPhase(pCCB)
CCB *pCCB;
{
	int i,j;
	uint32 rval ;

	if (pCCB->FRF9.Phase != FRF9_PHASE_OPERATIONAL) return 0 ;

	if (!(j = pCCB->FRF9.ContextCount)) j++ ;

	for(i = 0; i < j; i++) {
		/*
		 * Get a compression context.  Request a running history
		 * if the context count is greater than 0.
		 */
#if 0 // FRF.9 CDK
		if (!(pCCB->FRF9.FRF9_Context[i].Context =
		(uint32) StacGetNewContext(pCCB->FRF9.ContextCount)))
			goto FailedToStart ;
#else
		if (!(pCCB->FRF9.FRF9_Context[i].Context = StacGetNewContext(0)))
			goto FailedToStart ;
#endif

		/*
		 * Reset the context.
		 * The reset MAY be deferred, but that's okay.
		 */
		rval = StacResetHistory(pCCB->FRF9.FRF9_Context[i].Context,
			FRF9ResetCompleted, &(pCCB->FRF9.FRF9_Context[i])) ;
		if (!rval) {
			StacReleaseContext(pCCB->FRF9.FRF9_Context[i].Context) ;
			goto FailedToStart ;
		}

		if (rval == 1)
			FRF9ResetCompleted((int3264)&(pCCB->FRF9.FRF9_Context[i])) ;

		/*
		 * Initialize sequence numbers
		 */
		pCCB->FRF9.FRF9_Context[i].SequenceLastSent =
		pCCB->FRF9.FRF9_Context[i].SequenceExpected = 1 ;

		/*
		 * Intialize self-referential pointer
		 */
		pCCB->FRF9.FRF9_Context[i].ThisCCB = (caddr_t) pCCB ;
	}

	return 1 ;

FailedToStart:
	for(j = 0; j < i; j++) {
		StacReleaseContext(pCCB->FRF9.FRF9_Context[j].Context) ;
	}

	return 0 ;
}

/*
 * StopFRF9OperationPhase
 *
 * Purpose:  To shut down all data compression activities, and
 *           release contexts.
 *
 * Parameters:  pCCB -- A pointer to a CCB (aka DLCI) structure.
 *
 * Returns:   1 always.
 *
 * Notes:   No action is taken if session is not in the operation phase.
 *          The Phase state is set to Disabled.
 */

uint32
StopFRF9OperationPhase(pCCB)
CCB *pCCB;
{
	int i,j;

	if (pCCB->FRF9.Phase != FRF9_PHASE_OPERATIONAL) return 1 ;

	/*
	 * Demote phase back to disabled state.  This will prevent
	 * all compression/decompression activity.
	 */
	pCCB->FRF9.Phase = FRF9_PHASE_DISABLED ;

	/* Zero contexts is really one context with no running history */
	if (!(j = pCCB->FRF9.ContextCount)) j++ ;

	for(i = 0; i < j; i++) {
		StacReleaseContext(pCCB->FRF9.FRF9_Context[i].Context) ;
	}

	return 1 ;
}

/*
 * FRF9LaunchModeOne
 *
 * Purpose:  To start Mode-1 negotiation procedures with the peer entity.
 *
 * Parameters:  hCCB -- A 16-bit handle to a CCB (aka DLCI) structure.
 *
 * Returns:   N/A
 *
 * Notes:   This function automatically reschedules itself until
 *          the success or failure conditions are met.
 *
 *          The function taken different actions depending on the Phase:
 *          UNUSED -- Error condition.  Return immediately.
 *          DISABLED -- First call.  Initialize all counters and proceed
 *                      with first send.
 *          INITIALIZATION -- Timeout of a handshake attempt.
 *          OPERATIONAL -- Handshake completed.
 */

void
FRF9LaunchModeOne(hCCB)
u_long hCCB;
{
	CCB *pCCB ;
	mblk_t *mp ;
	ModeOneFrame_t *ModeOnePtr ;

	if (!(m_IS_PLL_VALID(hCCB, pCCB))) return ;

	if (pCCB->FRF9.Phase == FRF9_PHASE_UNUSED) {
		/* Launch request was made prematurely */
		return ;
	}

	if (pCCB->FRF9.Phase == FRF9_PHASE_OPERATIONAL) {
		/* This session has come on-line.  Our job is done. */
		return ;
	}

	if (pCCB->FRF9.Phase == FRF9_PHASE_DISABLED) {
		/* Negotiations have not started yet.  Initialize! */
		pCCB->FRF9.RetriesLeft = pCCB->FRF9.RetryCount ;
	} else {
		/*
		 * We were in the middle of initialization phase.
		 * Are we done trying yet?
		 */
		if (pCCB->FRF9.RetriesLeft-- == 0) {
			pCCB->FRF9.Phase = FRF9_PHASE_DISABLED ;
			return ;
		}
	}

	/*
	 * We must start over.  Reset the "response sent/received" flags.
	 */
	pCCB->FRF9.Phase = FRF9_PHASE_INIT ;

	/* Build a Mode-1 Frame */

	if (!(mp = allocb(sizeof(ModeOneFrame_t), BPRI_MED))) {
		return ;
	}

	ModeOnePtr = (ModeOneFrame_t *) mp->b_wptr ;
	mp->b_wptr += sizeof(ModeOneFrame_t) ;

	/* Q.922 address header, Control and NLPID bytes */
	ModeOnePtr->Q922MSB = pCCB->my_header.dlciMSB ;
	ModeOnePtr->Q922LSB = pCCB->my_header.dlciLSB ;
	ModeOnePtr->Control = FRAME_TYPE_03 ;
	ModeOnePtr->NLPID = DATA_COMPRESSION_NLPID ;

	/* DCP header */
	ModeOnePtr->DCPHeader = DCP_EXTENSION_BIT | DCP_CONTROL_BIT ;

	/* Mode One Request */
	ModeOnePtr->Code = MODE_ONE_REQ ;
	ModeOnePtr->ID = 0x00 ;
	ModeOnePtr->ReqLength[0] = 0x00 ;
	ModeOnePtr->ReqLength[1] = 0x07 ;
	ModeOnePtr->Type = 0xFE ;
	ModeOnePtr->ConfLength = 0x03 ;
	ModeOnePtr->Revision = 0x01 ;

	/*
	 * We do retransmissions, so we don't really care if
	 * this call succeeds or not.
	 */

	printf("FRF9 Negotiation Send out") ;

	FRSLAPD_SendData(hCCB, mp) ;
	pCCB->FRF9.FRF9_Stats.ModeOneReqSent++ ;

	/*
	 * Start timer
	 */
	FRSTMR_start((u_short) pCCB->FRF9.ModeOneTimer, 
		(u_short) (1000* (u_short) pCCB->FRF9.RetryPeriod)) ;
}

/*
 * FRF9RRTransmit
 *
 * Purpose:  To retransmit empty data frames with the Reset Request bit
 *           asserted, once a second.
 *
 * Parameters:  hCCB -- A 16-bit handle to a CCB (aka DLCI) structure.
 *
 * Returns:   N/A
 *
 * Notes:   This function automatically reschedules itself until
 *          the error condition for all DCCI's are cleared.
 *
 *          It _is_ possible (when using multiple DCCI's) to have this
 *          function called more than once per second, resulting in
 *          two or more reset requests going out in quick succession.
 *          This should not interfere with the normal operation of the
 *          peer FRF.9 entity.
 *
 *          This function terminates early if the FRF.9 session
 *          is not Operational, or if we are using no running histories.
 */

void
FRF9RRTransmit(hCCB)
u_long hCCB;
{
	CCB *pCCB ;
	mblk_t *mp ;
	FRF9_Context_t *FRContext ;
	uint16 i ;
	uint16 RRsExist = 0 ;

	if (!(m_IS_PLL_VALID(hCCB, pCCB))) return ;

	if ((pCCB->FRF9.Phase != FRF9_PHASE_OPERATIONAL)
	|| (!pCCB->FRF9.ContextCount)) {
		return ;
	}

	for(i = 0, FRContext = pCCB->FRF9.FRF9_Context; i < pCCB->FRF9.ContextCount; i++, FRContext++) {
		if (!(FRContext->RR_RA_State & SEND_RR)) continue ;
		RRsExist = 1 ;
		if (!(mp = allocb(SIZEOF_EMPTY_DATA_FRAME, BPRI_MED))) continue ;

		if (FRContext->RR_RA_State & SEND_RR)
			pCCB->FRF9.FRF9_Stats.RRSent++ ;
		if (FRContext->RR_RA_State & SEND_RA)
			pCCB->FRF9.FRF9_Stats.RASent++ ;

		*((FR_HDR *) mp->b_wptr) = pCCB->my_header ;
		mp->b_wptr += sizeof(FR_HDR) ;
		*mp->b_wptr++ = FRAME_TYPE_03 ;
		*mp->b_wptr++ = DATA_COMPRESSION_NLPID ;
		*mp->b_wptr++ = DCP_EXTENSION_BIT | FRContext->RR_RA_State ;
		UNSET_RA(FRContext->RR_RA_State) ;
		if (pCCB->FRF9.ContextCount > 1) {
			*mp->b_wptr++ = (uint8) ((i+1) >> 8) ;
			*mp->b_wptr++ = (uint8) ((i+1) & 0xFF) ;
		}

		/* We will keep sending them until something changes */
		FRSLAPD_SendData(hCCB, mp) ;
	}

	/*
	 * If at least one reset request is outstanding,
	 * check again in one second to see if they're all gone.
	 */
	if (RRsExist)
		FRSTMR_start((u_short) pCCB->FRF9.ResetTimer, 1000) ;
}

/*
 * TxFRF9Frame
 *
 * Purpose:  To compress a standard Frame Relay frame into a FRF.9 frame.
 *
 * Parameters:  pCCB -- A pointer to a CCB (aka DLCI) structure.
 *              Frame -- A STREAMS message block containing the target frame.
 *
 * Returns:   A pointer to an FRF.9 frame, which may or may not
 *            contain compressed data;
 *            0 if the compression operation was deferred, or failed;
 *            -1 if the compress operation could not start since another
 *            compress operation is still pending.
 *
 * Notes:   The parameter Frame will be freed by this function if and only
 *          if the return value is NOT -1.
 *
 *          This function assumes that the b_rptr element is pointing
 *          to the first address byte of the frame.
 *
 *          Even though we currently only use Mode One defaults, this
 *          function is prepared to deal with multiple contexts.
 *
 *          THIS IS THE START OF THE TRANSMIT SIDE'S HOT PATH!
 */

mblk_t *
TxFRF9Frame(pCCB, Frame)
CCB *pCCB;
mblk_t *Frame;
{
	mblk_t *Result = (mblk_t *) 0 ;
	FRF9_Context_t *FRContext ;
	mblk_t *rval ;
	uint8 XORByte ;
	uint8 CDBit = DCP_COMPRESSED_BIT ;
	int sz ;
	mblk_t *bp ;

	/*
	 * For now, we will only use the first DCCI, regardless of the
	 * number available to us.  When a "good" alternative algorithm
	 * is found, this section should be modified.
	 *
	 * Also, modify the section below where the DCCI is plugged into
	 * the new frame.
	 */
	FRContext = &(pCCB->FRF9.FRF9_Context[0]) ;

	if (COMPRESS_JOB_PENDING(FRContext->Context))
		return ((mblk_t *) -1) ;

	sz = (int)(Frame->b_rptr - Frame->b_datap->db_base) ;

	Frame->b_rptr = Frame->b_datap->db_base ;

	pullupmsg(Frame, -1) ;


	Frame->b_rptr += sz ;

	/* Move to beginning of NLPID */
	Frame->b_rptr += Q922_ADDR_LEN + 1 ;

	/* Get the full  size of the frame */
	for(bp = Frame, sz = 0 ; bp ; bp = bp->b_cont)
		sz += (int)(bp->b_wptr - bp->b_rptr) ;

	/* Compress the frame */
	rval = (mblk_t *) StacCompress(FRContext->Context, Frame,
	FRF9CompressFinished, FRContext, &XORByte) ;

	if (rval == (mblk_t *) -1) {
		/* Compression was deferred */
		FRContext->DefCompressFrame = Frame ;
	}
	else if (!rval) {
		/* Compression failed */
		pCCB->FRF9.FRF9_Stats.CompressionFailures++ ;
		printf("Compress Failed") ;
		if (StacResetHistory(FRContext->Context, FRF9ResetCompleted, FRContext) == 1)
			FRF9ResetCompleted((int3264)FRContext) ;
		freemsg(Frame) ;
	} else {
		/* Compression succeeded; build new frame */
		pCCB->FRF9.FRF9_Stats.PacketsCompressed++ ;

		/* Send in uncompressed mode if we didn't gain anything */
		if (((rval->b_wptr - rval->b_rptr) + 4) > sz) 
		{
			pCCB->FRF9.FRF9_Stats.CompressionsUndone++ ;
			if (!(pCCB->FRF9.CurrentMode & FRF9_TX_PMODE_COMP_UNCOMP)) {
				if (StacResetHistory(FRContext->Context, FRF9ResetCompleted, FRContext) == 1)
					FRF9ResetCompleted((int3264)FRContext) ;
			}
			Result = Frame ;
			freemsg((mblk_t *) rval) ;
			CDBit = 0 ;
		} else {
			Result = rval ;
			freemsg(Frame) ;

			/* This applies to compressed data only */
			if (pCCB->FRF9.CurrentMode & FRF9_TX_CKMODE_LCB) {
				*Result->b_wptr++ = XORByte ;
			}
		}

		if (pCCB->FRF9.CurrentMode & FRF9_TX_CKMODE_SEQ) {
			*--Result->b_rptr = FRContext->SequenceLastSent ;
			if (!++FRContext->SequenceLastSent)
				FRContext->SequenceLastSent++ ;
		}

		/* Plug in DCCI, if applicable */
		if (pCCB->FRF9.ContextCount > 1) {
			*--Result->b_rptr = 0x01 ;
			*--Result->b_rptr = 0x00 ;
		}

		if (FRContext->RR_RA_State & SEND_RR)
			pCCB->FRF9.FRF9_Stats.RRSent++ ;
		if (FRContext->RR_RA_State & SEND_RA)
			pCCB->FRF9.FRF9_Stats.RASent++ ;

		*--Result->b_rptr = DCP_EXTENSION_BIT | FRContext->RR_RA_State | CDBit ;
		UNSET_RA(FRContext->RR_RA_State) ;

		*--Result->b_rptr = DATA_COMPRESSION_NLPID ;
		*--Result->b_rptr = FRAME_TYPE_03 ;
		*--Result->b_rptr = pCCB->my_header.dlciLSB ;
		*--Result->b_rptr = pCCB->my_header.dlciMSB ;

	}

	return Result ;
}

/*
 * ParseFRF9Frame
 *
 * Purpose:  To take action on an inbound FRF.9 frame.
 *
 * Parameters:  pCCB -- A pointer to a CCB (aka DLCI) structure.
 *              Frame -- A STREAMS message block containing an FRF.9 frame.
 *
 * Returns:   A pointer to an uncompressed Frame Relay packet,
 *            if the frame contained valid compressed data;
 *            0 if the packet was a control frame, decompression
 *            was deferred, or the frame was invalid.
 *            -1 if the decompress operation could not start since another
 *            decompress operation is still pending.
 *
 * Notes:   The parameter Frame will be freed by this function if and only
 *          if the return value is NOT -1.  If the value returned IS -1,
 *          then the caller should stick the frame back on the head of
 *          the read-side queuing mechanism.  When any one of the contexts
 *          for this DLCI finishes its decompress operation, it will call
 *          sys_pll_rcvd_frame(), and another decompression attempt will
 *          be made.  See FRF9DecompressFinished().
 *
 *          This function assumes that the b_rptr element is pointing
 *          to the control byte of the frame.
 *
 *          Even though we currently only use Mode One defaults, this
 *          function is prepared to deal with multiple contexts.
 *
 *          THIS IS THE START OF THE RECEIVE SIDE'S HOT PATH!
 */

mblk_t *
ParseFRF9Frame(pCCB, Frame)
CCB *pCCB;
mblk_t *Frame;
{

	mblk_t *Result = (mblk_t *) 0;
	ModeOneFrame_t *MOF ;
	FRF9_Context_t *FRContext ;
	uint8 *LookAhead ;
	uint3264 rval ;
	uint16 DCCI ;
	uint8 DCPHeader ;
	uint8 XORByte ;
	int i ;
	char *badseqheader ; 

	if(!Frame->b_rptr)
	{
		goto EndOfParse ;
		printf("Frf Parse 1") ;
	}
	/* Move back to the very beginning of the frame */
	MOF = (ModeOneFrame_t *) (Frame->b_rptr -= Q922_ADDR_LEN) ;

	/* E-Bit must always be asserted, according to the document */
	if (!(MOF->DCPHeader & DCP_EXTENSION_BIT)) {
		pCCB->FRF9.FRF9_Stats.NoEBit++ ;
		printf("Frf Parse 2") ;
		goto EndOfParse ;
	}

	if (MOF->DCPHeader & DCP_CONTROL_BIT) {
		FRF9RxControl(pCCB, Frame) ;
		printf("Frf Parse 3") ;
		goto EndOfParse ;
	}

	/*
	 *  This appears to be a data frame!
	 */

	/* Is FRF.9 operational for this link? */
	if (pCCB->FRF9.Phase != FRF9_PHASE_OPERATIONAL)
	{
		printf("FRf Parse 4") ;
		goto EndOfParse ;
	}

	/* Examine DCP header */
	DCPHeader = MOF->DCPHeader ;

	/* Move beyond header */
	LookAhead = Frame->b_rptr + Q922_ADDR_LEN + 3 ;

	/* Get DCCI, if appropriate */
	if (pCCB->FRF9.ContextCount > 1) {
		/* Must extract DCCI; note it's lack of 16-bit alignment */
		DCCI = ((((uint16) *LookAhead) << 8) | ((uint16) *(LookAhead + 1))) - 1 ;
		if (DCCI >= pCCB->FRF9.ContextCount)
		{
			goto EndOfParse ;
		}
		LookAhead += 2 ;
	} else {
		DCCI = 0 ;
	}

	FRContext = &(pCCB->FRF9.FRF9_Context[DCCI]) ;

	/*
	 * See if another decompress job is still pending for this
	 * context.  If so, return -1 to inform the calling function
	 * that it should put the frame back on the head of the receive
	 * queue.
	 */
	if (DECOMPRESS_JOB_PENDING(FRContext->Context)) {
		return ((mblk_t *) -1) ;
	}

	/*
	 * Check RA bit
	 */
	if (DCPHeader & DCP_RESET_ACK_BIT) {
		/* Clear local error condition and resync sequence number */
		UNSET_RR(FRContext->RR_RA_State) ;
		if (pCCB->FRF9.CurrentMode & FRF9_RX_CKMODE_SEQ) 
		{
			FRContext->SequenceExpected = 0 ;
		}
		pCCB->FRF9.FRF9_Stats.RAReceived++ ;
	}

	/*
	 * Check RR bit
	 */
	if (DCPHeader & DCP_RESET_REQUEST_BIT) {
		if (StacResetHistory(FRContext->Context, FRF9ResetCompleted, FRContext) == 1)
			FRF9ResetCompleted((int3264)FRContext) ;
		pCCB->FRF9.FRF9_Stats.RRReceived++ ;
	}

	/*
	 * We could be in the middle of an error condition
	 */
	if (FRContext->RR_RA_State & SEND_RR) {
		pCCB->FRF9.FRF9_Stats.DropsDuringReset++ ;
		goto EndOfParse ;
	}

	/*
	 * This COULD be an empty data frame
	 */
	if (LookAhead >= Frame->b_wptr) 
	{
		printf("Frf Parse 7") ;
		goto EndOfParse ;
	}

	/*
	 * Frame contains data.  Check the sequence numbers.
	 */
	if (pCCB->FRF9.CurrentMode & FRF9_RX_CKMODE_SEQ) {
		/* Resync if necessary */
		if (!FRContext->SequenceExpected)
			FRContext->SequenceExpected = *LookAhead++ ;

		/* If sequence numbers do not match, enter error state */
		else if (FRContext->SequenceExpected != *LookAhead++) {
			pCCB->FRF9.FRF9_Stats.SequenceFailures++ ;
			SET_RR(FRContext->RR_RA_State) ;
			FRF9RRTransmit(pCCB->hCCB) ;
			printf("FRFPARSE Sequence Num %x,%x",*LookAhead,FRContext->SequenceExpected) ;
			badseqheader = Frame->b_rptr ;
			for(i = 0 ; i < 8 ; i++)
				printf("%x",*badseqheader++) ;

			goto EndOfParse ;
		}
		/* Advance receive sequence, skipping 0 */
		if (!++FRContext->SequenceExpected)
			FRContext->SequenceExpected++ ;
	}

	/*
	 * Move to start of data payload
	 */
	Frame->b_rptr = LookAhead ;

	/*
	 * Is it REALLY compressed data?
	 */
	if (DCPHeader & DCP_COMPRESSED_BIT) {
		/* Compressed data */

		/* Yank XOR Byte if it is present, save it for later */
		if (pCCB->FRF9.CurrentMode & FRF9_RX_CKMODE_LCB)
			Frame->b_wptr-- ;

		/* Perform actual decompression */
		rval = StacDecompress(FRContext->Context, Frame, FRF9DecompressFinished, FRContext, &XORByte) ;
		if (!rval) {
			/* Failure... request remote reset */
			pCCB->FRF9.FRF9_Stats.DecompressionFailures++ ;
			SET_RR(FRContext->RR_RA_State) ;
			FRF9RRTransmit(pCCB->hCCB) ;
			printf("Frf Parse 7.5") ;
		}
		else if (rval == -1) {
			/* Deferred... save our place and leave */
			FRContext->DefDecompressFrame = Frame ;
			return Result ;
		}
		else {
			/* Immediate results; check LCB if applicable */
			pCCB->FRF9.FRF9_Stats.PacketsDecompressed++ ;

			if ((pCCB->FRF9.CurrentMode & FRF9_RX_CKMODE_LCB) &&
			(*Frame->b_wptr != XORByte)) {
				pCCB->FRF9.FRF9_Stats.LCBFailures++ ;
				freemsg((mblk_t *) rval) ;
				SET_RR(FRContext->RR_RA_State) ;
				FRF9RRTransmit(pCCB->hCCB) ;
				printf("Frf Parse 8") ;
			} else {
				Result = (mblk_t *) rval ;

				/* Affix Frame Relay header */
				*--Result->b_rptr = FRAME_TYPE_03 ;
				*--Result->b_rptr = pCCB->my_header.dlciLSB ;
				*--Result->b_rptr = pCCB->my_header.dlciMSB ;

				freemsg(Frame) ;
			}
		}
	} else {
		/* Non-compressed data */

		/*
		 * At this point, the code SHOULD branch based off of the
		 * process mode (Comp only vs Comp+Uncomp).  However, the
		 * STAC hardware is incapable of offering the Comp+Uncomp
		 * option, so we cannot code for it right now.
		 *
		 * If this option ever becomes available (and this code
		 * evolves beyond Mode-1 negotiation), the appropriate
		 * code should be placed here.
		 */

		pCCB->FRF9.FRF9_Stats.UncompressedPackets++ ;

		/* Affix Frame Relay header */
		*--Frame->b_rptr = FRAME_TYPE_03 ;
		*--Frame->b_rptr = pCCB->my_header.dlciLSB ;
		*--Frame->b_rptr = pCCB->my_header.dlciMSB ;

		/* Don't free Frame, it's our output */
		return Frame ;
	}

EndOfParse:

	return Result ;
}

/*
 * FRF9RxControl
 *
 * Purpose:  To process an inbound FRF.9 control packet.
 *
 * Parameters:  pCCB -- A pointer to a CCB (aka DLCI) structure.
 *              Frame -- A STREAMS message block containing an FRF.9 frame.
 *
 * Returns:   N/A
 *
 * Notes:   The parameter Frame is NOT freed by this function.
 *
 *          This function assumes that the b_rptr element is pointing
 *          to the first address byte of the frame.
 *
 *          This function could potentially restart FRF9LaunchModeOne().
 */

void
FRF9RxControl(pCCB, Frame)
CCB *pCCB;
mblk_t *Frame;
{
	ModeOneFrame_t *MOF = (ModeOneFrame_t *) Frame->b_rptr ;
	ModeOneFrame_t *Resp ;
	mblk_t *RBlk ;
	uint8 PreviousPhase = pCCB->FRF9.Phase ;

	/* Check to see if this is REALLY a valid Mode One frame */
	if (Frame->b_wptr - Frame->b_rptr >= sizeof(ModeOneFrame_t) &&
	(MOF->Code == MODE_ONE_REQ || MOF->Code == MODE_ONE_RESP) &&
	MOF->ID == 0x00 &&
	MOF->ReqLength[0] == 0x00 &&
	MOF->ReqLength[1] == 0x07 &&
	MOF->Type == 0xFE &&
	MOF->ConfLength == 0x03) {
		if (MOF->Code == MODE_ONE_REQ) {
			pCCB->FRF9.FRF9_Stats.ModeOneReqRcvd++ ;

			/* If we were operational, we need to start over! */
			if (pCCB->FRF9.Phase == FRF9_PHASE_OPERATIONAL) {
				StopFRF9OperationPhase(pCCB) ;
			}

			/* If the user does not want compression, fine. */
			if (!pCCB->FRF9.RetryPeriod) return ;

			/* If we are not already in INIT, restart Mode One */
			if (pCCB->FRF9.Phase == FRF9_PHASE_DISABLED) {
				FRF9LaunchModeOne(pCCB->hCCB) ;
			}

			/* Generate response frame */
			if (!(RBlk = allocb(sizeof(ModeOneFrame_t), BPRI_MED)))
				return ;

			Resp = (ModeOneFrame_t *) RBlk->b_wptr ;
			RBlk->b_wptr += sizeof(ModeOneFrame_t) ;
			Resp->Q922MSB = pCCB->my_header.dlciMSB ;
			Resp->Q922LSB = pCCB->my_header.dlciLSB ;
			Resp->Control = FRAME_TYPE_03 ;
			Resp->NLPID = DATA_COMPRESSION_NLPID ;
			Resp->DCPHeader = DCP_EXTENSION_BIT | DCP_CONTROL_BIT ;
			Resp->Code = MODE_ONE_RESP ;
			Resp->ID = 0x00 ;
			Resp->ReqLength[0] = 0x00 ;
			Resp->ReqLength[1] = 0x07 ;
			Resp->Type = 0xFE ;
			Resp->ConfLength = 0x03 ;
			Resp->Revision = 0x01 ;

			/*
			 * If this fails, that's okay.  The remote
			 * site should eventually retransmit a request.
			 */
			FRSLAPD_SendData(pCCB->hCCB, RBlk) ;
			pCCB->FRF9.FRF9_Stats.ModeOneRespSent++ ;

			pCCB->FRF9.Phase |= FRF9_PHASE_RESP_SENT ;
		} else {
			/* Got a response frame */
			pCCB->FRF9.FRF9_Stats.ModeOneRespRcvd++ ;
			pCCB->FRF9.Phase |= FRF9_PHASE_RESP_RCVD ;
		}
	} else
		pCCB->FRF9.FRF9_Stats.UnknownCtrlFrames++ ;

	/*
	 * Have we made the transition from Initialization to Operational?
	 * Is so, we need to get our compression contexts from the STAC
	 * library.
	 *
	 * Set the CurrentMode and ContextCount to Mode-1 Defaults.
	 */
	if (PreviousPhase != FRF9_PHASE_OPERATIONAL &&
		pCCB->FRF9.Phase == FRF9_PHASE_OPERATIONAL) {
		pCCB->FRF9.ContextCount = 1 ;
		pCCB->FRF9.CurrentMode = FRF9_TX_CKMODE_SEQ_LCB |
					FRF9_TX_PMODE_COMP_ONLY |
					FRF9_RX_CKMODE_SEQ_LCB |
					FRF9_RX_PMODE_COMP_ONLY ;

		/* If a problem occurs, start over */
		if (!StartFRF9OperationPhase(pCCB)) {
			pCCB->FRF9.Phase = FRF9_PHASE_INIT ;
			FRF9LaunchModeOne(pCCB->hCCB) ;
		}
	}
}

void
FRF9ResetCompleted(ContextAddr)
uint3264 ContextAddr;
{
	FRF9_Context_t *FRContext = (FRF9_Context_t *) ContextAddr ;

	SET_RA(FRContext->RR_RA_State) ;
}

void FRF9DecompressFinished(ContextAddr)
uint32 ContextAddr;
{
	FRF9_Context_t *FRContext = (FRF9_Context_t *) ContextAddr ;
	CCB *pCCB = (CCB *) FRContext->ThisCCB ;
	mblk_t *mp ;
	uint8 XORByte ;

	if (!(mp = (mblk_t *) StacGetDecompressResults(FRContext->Context, &XORByte))) {
		/* Failure... request remote reset */
		pCCB->FRF9.FRF9_Stats.DecompressionFailures++ ;
		SET_RR(FRContext->RR_RA_State) ;
		FRF9RRTransmit(pCCB->hCCB) ;
	} else {
		pCCB->FRF9.FRF9_Stats.PacketsDecompressed++ ;

		if ((pCCB->FRF9.CurrentMode & FRF9_RX_CKMODE_LCB) &&
		(*FRContext->DefDecompressFrame->b_wptr != XORByte)) {
			pCCB->FRF9.FRF9_Stats.LCBFailures++ ;
			freemsg(mp) ;
			SET_RR(FRContext->RR_RA_State) ;
			FRF9RRTransmit(pCCB->hCCB) ;
		}

		/* Affix Frame Relay header */
		*--mp->b_rptr = FRAME_TYPE_03 ;
		*--mp->b_rptr = pCCB->my_header.dlciLSB ;
		*--mp->b_rptr = pCCB->my_header.dlciMSB ;

		/* place this frame on the head of the CCB read queue */
		FRSQTEnqHead(mp, &(pCCB->Read_Queue)) ;

		/* Now tweak the receive engine again */
		sys_pll_rcvd_frame(pCCB) ;
	}

	/* Free the original frame */
	freemsg(FRContext->DefDecompressFrame) ;
}

void FRF9CompressFinished(ContextAddr)
uint32 ContextAddr;
{
	FRF9_Context_t *FRContext = (FRF9_Context_t *) ContextAddr ;
	CCB *pCCB = (CCB *) FRContext->ThisCCB ;
	mblk_t *Frame = FRContext->DefCompressFrame ;
	mblk_t *Result ;
	mblk_t *mp ;
	uint8 XORByte ;
	uint8 CDBit ;

	/* If compression failed, perform a local reset */
	if (!(mp = (mblk_t *) StacGetCompressResults((StacContext_t *)FRContext->Context, &XORByte))) {
		pCCB->FRF9.FRF9_Stats.CompressionFailures++ ;
		if (StacResetHistory(FRContext->Context, FRF9ResetCompleted, FRContext) == 1)
			FRF9ResetCompleted((int3264)FRContext);

		freemsg(Frame) ;
		goto GetNextFrames ;
	}

	/* Compression succeeded; build new frame */
	pCCB->FRF9.FRF9_Stats.PacketsCompressed++ ;

	/* Send in uncompressed mode if we didn't gain anything */
	if ((mp->b_wptr - mp->b_rptr) + 4 >
	Frame->b_wptr - Frame->b_rptr) {
		pCCB->FRF9.FRF9_Stats.CompressionsUndone++ ;
		if (!(pCCB->FRF9.CurrentMode & FRF9_TX_PMODE_COMP_UNCOMP)) {
			if (StacResetHistory(FRContext->Context, FRF9ResetCompleted, FRContext) == 1)
				FRF9ResetCompleted((int3264) FRContext) ;
		}
		Result = Frame ;
		freemsg(mp) ;
		CDBit = 0 ;
	} else {
		Result = mp ;
		freemsg(Frame) ;

		/* This applies to compressed data only */
		if (pCCB->FRF9.CurrentMode & FRF9_TX_CKMODE_LCB) {
			*Result->b_wptr++ = XORByte ;
		}
		CDBit = 1 ;
	}

	if (pCCB->FRF9.CurrentMode & FRF9_TX_CKMODE_SEQ) {
		*--Result->b_rptr = FRContext->SequenceLastSent ;
		if (!++FRContext->SequenceLastSent)
			FRContext->SequenceLastSent++ ;
	}

	/* Plug in DCCI, if applicable */
	if (pCCB->FRF9.ContextCount > 1) {
		*--Result->b_rptr = 0x01 ;
		*--Result->b_rptr = 0x00 ;
	}

	if (FRContext->RR_RA_State & SEND_RR)
		pCCB->FRF9.FRF9_Stats.RRSent++ ;
	if (FRContext->RR_RA_State & SEND_RA)
		pCCB->FRF9.FRF9_Stats.RASent++ ;

	*--Result->b_rptr = DCP_EXTENSION_BIT | FRContext->RR_RA_State | CDBit ;
	UNSET_RA(FRContext->RR_RA_State) ;

	*--Result->b_rptr = DATA_COMPRESSION_NLPID ;
	*--Result->b_rptr = FRAME_TYPE_03 ;
	*--Result->b_rptr = pCCB->my_header.dlciLSB ;
	*--Result->b_rptr = pCCB->my_header.dlciMSB ;

	FRSLAPD_SendData(pCCB->hCCB, Result) ;

GetNextFrames:
	while (mp = FRSQTDequeue((QTCB *) &pCCB->FRF9.TxQueue)) {

		Result = TxFRF9Frame(pCCB, mp) ;
	
		if (!Result) break ;
		else if (Result == (mblk_t *) -1) {
			FRSQTEnqHead(mp, (QTCB *) &pCCB->FRF9.TxQueue) ;
			break ;
		} else {
			FRSLAPD_SendData(pCCB->hCCB, Result) ;
		}
	}
}

