Ejemplo n.º 1
0
/*
 * Send a UNISIG signalling message
 *
 * Called to send a Q.2931 message.  This routine encodes the message
 * and hands it to SSCF for transmission.
 *
 * Arguments:
 *	usp	pointer to UNISIG protocol instance block
 *	msg	pointer to message
 *
 * Returns:
 *	0	message sent OK
 *	errno	error encountered
 *
 */
int
unisig_send_msg(struct unisig *usp, struct unisig_msg *msg)
{
	int		err = 0;
	struct usfmt	usf;

	ATM_DEBUG2("unisig_send_msg: msg=%p, type=%d\n", msg,
			msg->msg_type);

	/*
	 * Make sure the network is up
	 */
	if (usp->us_state != UNISIG_ACTIVE)
		return(ENETDOWN);

#ifdef DIAGNOSTIC
	/*
	 * Print the message we're sending.
	 */
	if (unisig_print_msg)
		usp_print_msg(msg, UNISIG_MSG_OUT);
#endif

	/*
	 * Convert message to network order
	 */
	err = usf_init(&usf, usp, NULL, USF_ENCODE,
			usp->us_headout);
	if (err)
		return(err);

	err = usf_enc_msg(&usf, msg);
	if (err) {
		ATM_DEBUG1("unisig_send_msg: encode failed with %d\n",
				err);
		KB_FREEALL(usf.usf_m_base);
		return(EIO);
	}

#ifdef DIAGNOSTIC
	/*
	 * Print the converted message
	 */
	if (unisig_print_msg > 1)
		unisig_print_mbuf(usf.usf_m_base);
#endif

	/*
	 * Send the message
	 */
	err = atm_cm_saal_data(usp->us_conn, usf.usf_m_base);
	if (err)
		KB_FREEALL(usf.usf_m_base);

	return(err);
}
Ejemplo n.º 2
0
/*
 * RS PDU / SOS_READY Processor
 * 
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
static void
sscop_rs_ready(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct rs_pdu	*rp = (struct rs_pdu *)trlr;
	int		err;

	/*
	 * If retransmitted RS, just ACK it
	 */
	if (sscop_is_rexmit(sop, rp->rs_nsq)) {
		KB_FREEALL(m);
		sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
		sscop_send_rsak(sop);
		return;
	}

	/*
	 * Stop data transfer timers
	 */
	sop->so_timer[SSCOP_T_POLL] = 0;
	sop->so_timer[SSCOP_T_NORESP] = 0;
	sop->so_timer[SSCOP_T_IDLE] = 0;
	sop->so_flags &= ~SOF_KEEPALIVE;

	/*
	 * Initialize transmit window
	 */
	SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr));

	/*
	 * Notify user of connection resynchronization
	 */
	STACK_CALL(SSCOP_RESYNC_IND, sop->so_upper, sop->so_toku, 
		sop->so_connvc, (int)m, 0, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Clear out appropriate queues
	 */
	q2110_prep_retrieve(sop);

	/*
	 * Wait for user response
	 */
	sop->so_state = SOS_INRESYN;

	return;
}
Ejemplo n.º 3
0
/*
 * POLL PDU / SOS_READY Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
static void
sscop_poll_ready(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct poll_pdu	*pp = (struct poll_pdu *)trlr;
	sscop_seq	nps;

	pp->poll_ns = ntohl(pp->poll_ns);

	/*
	 * If the poll sequence number is less than highest number
	 * we've already seen, something's wrong
	 */
	if (SEQ_LT(pp->poll_ns, sop->so_rcvhigh, sop->so_rcvnext)) {
		/*
		 * Record error condition
		 */
		sscop_maa_error(sop, 'Q');

		/*
		 * Free buffers
		 */
		KB_FREEALL(m);

		/*
		 * Go into recovery mode
		 */
		q2110_error_recovery(sop);

		return;
	}

	/*
	 * Set a new "next highest" sequence number expected
	 */
	if (SEQ_LT(pp->poll_ns, sop->so_rcvmax, sop->so_rcvnext))
		SEQ_SET(sop->so_rcvhigh, pp->poll_ns);
	else
		sop->so_rcvhigh = sop->so_rcvmax;

	/*
	 * Return a STAT PDU to peer
	 */
	SEQ_SET(nps, ntohl(pp->poll_nps));
	KB_FREEALL(m);
	sscop_send_stat(sop, nps);

	return;
}
Ejemplo n.º 4
0
/*
 * ENDAK PDU / SOS_OUTDISC Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_endak_outdisc(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	int		err;

	/*
	 * Stop retransmit timer
	 */
	sop->so_timer[SSCOP_T_CC] = 0;

	/*
	 * Release buffers
	 */
	KB_FREEALL(m);

	/*
	 * Notify user of connection termination
	 */
	STACK_CALL(SSCOP_RELEASE_CNF, sop->so_upper, sop->so_toku,
		sop->so_connvc, 0, 0, err);
	if (err) {
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Back to idle state
	 */
	sop->so_state = SOS_IDLE;

	return;
}
Ejemplo n.º 5
0
/*
 * SSCOP Upper Stack Command Handler
 * 
 * This function will receive all of the stack commands issued from the 
 * layer below SSCOP (ie. CPCS).  Currently, only incoming PDUs will be
 * received here.  The appropriate processing function will be determined 
 * based on the received PDU type and the current sscop control block state.
 *
 * Arguments:
 *	cmd	stack command code
 *	tok	session token
 *	arg1	command specific argument
 *	arg2	command specific argument
 *
 * Returns:
 *	none
 *
 */
void
sscop_upper(int cmd, void *tok, int arg1, int arg2)
{
	struct sscop	*sop = (struct sscop *)tok;
	void		(**ptab) (struct sscop *, KBuffer *, caddr_t);
	void		(*func) (struct sscop *, KBuffer *, caddr_t);
	caddr_t		trlr;
	int		type;

	ATM_DEBUG5("sscop_upper: cmd=0x%x, sop=%p, state=%d, arg1=0x%x, arg2=0x%x\n",
		cmd, sop, sop->so_state, arg1, arg2);

	switch (cmd) {

	case CPCS_UNITDATA_SIG:
		/*
		 * Decode/validate received PDU
		 */
		trlr = sscop_pdu_receive((KBuffer *)arg1, sop, &type);
		if (trlr == NULL) {
			return;
		}

		/*
		 * Validate sscop state
		 */
		if (sop->so_state > SOS_MAXSTATE) {
			log(LOG_ERR, 
				"sscop_upper: invalid state sop=%p, state=%d\n",
				sop, sop->so_state);
			KB_FREEALL((KBuffer *)arg1);
			return;
		}

		/*
		 * Call event processing function
		 */
		ptab = sop->so_vers == SSCOP_VERS_QSAAL ?
				sscop_qsaal_pdutab[type]:
				sscop_q2110_pdutab[type];
		func = ptab[sop->so_state];
		if (func == NULL) {
			log(LOG_ERR, 
				"sscop_upper: unsupported pdu=%d, state=%d\n",
				type, sop->so_state);
			break;
		}
		(*func)(sop, (KBuffer *)arg1, trlr);
		break;

	default:
		log(LOG_ERR, "sscop_upper: unknown cmd 0x%x, sop=%p\n",
			cmd, sop);
	}

	return;
}
Ejemplo n.º 6
0
/*
 * No-op Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_noop(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	/*
	 * Just free PDU
	 */
	KB_FREEALL(m);

	return;
}
Ejemplo n.º 7
0
/*
 * BGREJ PDU / SOS_OUTCONN Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_bgrej_outconn(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	int		source, uu, err;

	/*
	 * Stop retransmit timer
	 */
	sop->so_timer[SSCOP_T_CC] = 0;

	if (sop->so_vers == SSCOP_VERS_QSAAL) {
		/*
		 * Clear reestablishment flag
		 */
		sop->so_flags &= ~SOF_REESTAB;

		KB_FREEALL(m);
		m = NULL;
		uu = SSCOP_UU_NULL;
		source = SSCOP_SOURCE_SSCOP;
	} else {
		uu = (int)m;
		source = SSCOP_SOURCE_USER;
	}

	/*
	 * Notify user of connection failure
	 */
	STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
		sop->so_connvc, uu, source, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Back to idle state
	 */
	sop->so_state = SOS_IDLE;

	return;
}
Ejemplo n.º 8
0
/*
 * BGN PDU / SOS_INCONN Processor
 * 
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
static void
sscop_bgn_inconn(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct bgn_pdu	*bp = (struct bgn_pdu *)trlr;
	int		err;

	/*
	 * If retransmitted BGN, ignore it
	 */
	if (sscop_is_rexmit(sop, bp->bgn_nsq)) {
		KB_FREEALL(m);
		return;
	}

	/*
	 * Initialize transmit window
	 */
	SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr));

	/*
	 * First, tell user current connection has been released
	 */
	STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, 
		sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_USER, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Now, tell user of new connection establishment
	 */
	STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku, 
		sop->so_connvc, (int)m, 0, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	return;
}
Ejemplo n.º 9
0
/*
 * MD PDU / SOS_* Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_md_all(struct sscop *sop, KBuffer *m, caddr_t trlr)
{

	/*
	 * We don't support MD PDUs
	 */
	KB_FREEALL(m);
	return;
}
Ejemplo n.º 10
0
/*
 * BGREJ PDU / Protocol Error
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_bgrej_error(struct sscop *sop, KBuffer *m, caddr_t trlr)
{

	/*
	 * Record error condition
	 */
	sscop_maa_error(sop, 'D');
	KB_FREEALL(m);
	return;
}
Ejemplo n.º 11
0
/*
 * No-op Processor (arg1 == buffer)
 * 
 * Arguments:
 *	sop	pointer to sscop connection block
 *	arg1	command-specific argument (buffer pointer)
 *	arg2	command-specific argument
 *
 * Returns:
 *	none
 *
 */
void
sscop_aa_noop_1(struct sscop *sop, int arg1, int arg2)
{

	/*
	 * Just free buffer chain
	 */
	if (arg1)
		KB_FREEALL((KBuffer *)arg1);

	return;
}
Ejemplo n.º 12
0
/*
 * UD PDU / SOS_* Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_ud_all(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	int		err;

	/*
	 * Pass data up to user
	 */
	STACK_CALL(SSCOP_UNITDATA_IND, sop->so_upper, sop->so_toku,
		sop->so_connvc, (int)m, 0, err);
	if (err)
		KB_FREEALL(m);
	return;
}
Ejemplo n.º 13
0
/*
 * END PDU / SOS_READY Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
static void
sscop_end_ready(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct end_pdu	*ep = (struct end_pdu *)trlr;
	int		err, source;

	/*
	 * Stop data transfer timers
	 */
	sop->so_timer[SSCOP_T_POLL] = 0;
	sop->so_timer[SSCOP_T_NORESP] = 0;
	sop->so_timer[SSCOP_T_IDLE] = 0;
	sop->so_flags &= ~SOF_KEEPALIVE;

	/*
	 * Acknowledge END
	 */
	sscop_send_endak(sop);

	/*
	 * Get Source value
	 */
	if (ep->end_type & PT_SOURCE_SSCOP)
		source = SSCOP_SOURCE_SSCOP;
	else
		source = SSCOP_SOURCE_USER;

	/*
	 * Notify user of connection termination
	 */
	STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
		sop->so_connvc, (int)m, source, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Clear out appropriate queues
	 */
	q2110_prep_retrieve(sop);

	/*
	 * Back to idle state
	 */
	sop->so_state = SOS_IDLE;

	return;
}
Ejemplo n.º 14
0
/*
 * END PDU / SOS_IDLE Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_end_idle(struct sscop *sop, KBuffer *m, caddr_t trlr)
{

	/*
	 * Free buffers
	 */
	KB_FREEALL(m);

	/*
	 * Return an ENDAK to peer
	 */
	sscop_send_endak(sop);

	return;
}
Ejemplo n.º 15
0
/*
 * RS PDU / SOS_OUTRECOV Processor
 * 
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
static void
sscop_rs_outrecov(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct rs_pdu	*rp = (struct rs_pdu *)trlr;
	int		err;

	/*
	 * If retransmitted RS, report an error
	 */
	if (sscop_is_rexmit(sop, rp->rs_nsq)) {
		sscop_rs_error(sop, m, trlr);
		return;
	}

	/*
	 * Stop retransmit timer
	 */
	sop->so_timer[SSCOP_T_CC] = 0;

	/*
	 * Initialize transmit window
	 */
	SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr));

	/*
	 * Notify user of connection resynchronization
	 */
	STACK_CALL(SSCOP_RESYNC_IND, sop->so_upper, sop->so_toku, 
		sop->so_connvc, (int)m, 0, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Clear receiver buffer
	 */
	sscop_rcvr_drain(sop);

	/*
	 * Wait for user response
	 */
	sop->so_state = SOS_INRESYN;

	return;
}
Ejemplo n.º 16
0
/*
 * END PDU / SOS_OUTRECOV Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
static void
sscop_end_outrecov(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct end_pdu	*ep = (struct end_pdu *)trlr;
	int		err, source;

	/*
	 * Stop retransmit timer
	 */
	sop->so_timer[SSCOP_T_CC] = 0;

	/*
	 * Acknowledge END
	 */
	sscop_send_endak(sop);

	/*
	 * Get Source value
	 */
	if (ep->end_type & PT_SOURCE_SSCOP)
		source = SSCOP_SOURCE_SSCOP;
	else
		source = SSCOP_SOURCE_USER;

	/*
	 * Notify user of connection termination
	 */
	STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
		sop->so_connvc, (int)m, source, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Clear receiver buffer
	 */
	sscop_rcvr_drain(sop);

	/*
	 * Back to idle state
	 */
	sop->so_state = SOS_IDLE;

	return;
}
Ejemplo n.º 17
0
/*
 * ERAK PDU / SOS_OUTRECOV Processor
 * 
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
static void
sscop_erak_outrecov(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct erak_pdu	*ep = (struct erak_pdu *)trlr;
	int		err;

	/*
	 * Stop retransmit timer
	 */
	sop->so_timer[SSCOP_T_CC] = 0;

	/*
	 * Initialize transmit window
	 */
	SEQ_SET(sop->so_sendmax, ntohl(ep->erak_nmr));

	/*
	 * Free PDU buffers
	 */
	KB_FREEALL(m);

	/*
	 * Deliver any outstanding data to user
	 */
	q2110_deliver_data(sop);

	/*
	 * Notify user of connection recovery
	 */
	STACK_CALL(SSCOP_RECOVER_IND, sop->so_upper, sop->so_toku, 
		sop->so_connvc, 0, 0, err);
	if (err) {
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Wait for user response
	 */
	sop->so_state = SOS_RECOVRSP;

	return;
}
Ejemplo n.º 18
0
/*
 * RS PDU / SOS_INRESYN Processor
 * 
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
static void
sscop_rs_inresyn(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct rs_pdu	*rp = (struct rs_pdu *)trlr;

	/*
	 * If retransmitted RS, ignore it
	 */
	if (sscop_is_rexmit(sop, rp->rs_nsq)) {
		KB_FREEALL(m);
		return;
	}

	/*
	 * Report error condition
	 */
	sscop_rs_error(sop, m, trlr);

	return;
}
Ejemplo n.º 19
0
/*
 * ER PDU / SOS_INRECOV Processor
 * 
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
static void
sscop_er_inrecov(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct er_pdu	*ep = (struct er_pdu *)trlr;

	/*
	 * If retransmitted ER, just ignore it
	 */
	if (sscop_is_rexmit(sop, ep->er_nsq)) {
		KB_FREEALL(m);
		return;
	}

	/*
	 * Report error condition
	 */
	sscop_er_error(sop, m, trlr);

	return;
}
Ejemplo n.º 20
0
/*
 * Issue an ATMARP NAK PDU
 * 
 * Arguments:
 *	uip	pointer to IP interface
 *	m	pointer to ATMARP_REQ buffer chain
 *	ivp	pointer to vcc over which to send pdu
 *
 * Returns:
 *	0	PDU was successfully sent
 *	else	unable to send PDU
 *
 */
int
uniarp_arp_nak(struct uniip *uip, KBuffer *m, struct ipvcc *ivp)
{
	struct atmarp_hdr	*ahp;
	int		err;

	/*
	 * Get the fixed fields together
	 */
	if (KB_LEN(m) < sizeof(struct atmarp_hdr)) {
		KB_PULLUP(m, sizeof(struct atmarp_hdr), m);
		if (m == NULL)
			return (1);
	}
	KB_DATASTART(m, ahp, struct atmarp_hdr *);

	/*
	 * Set new op-code
	 */
	ahp->ah_op = htons(ARP_NAK);

	/*
	 * Finally, send the pdu to the vcc peer
	 */
	if (uniarp_print)
		uniarp_pdu_print(ivp, m, "send");
	err = atm_cm_cpcs_data(ivp->iv_arpconn, m);
	if (err) {
		/*
		 * Didn't make it
		 */
		KB_FREEALL(m);
		return (1);
	}

	return (0);
}
Ejemplo n.º 21
0
/*
 * BGN PDU / SOS_IDLE Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_bgn_idle(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct bgn_pdu	*bp = (struct bgn_pdu *)trlr;
	int		err, source;

	if (sop->so_vers == SSCOP_VERS_Q2110) {
		/*
		 * "Power-up Robustness" option
		 *
		 * Accept BGN regardless of BGN.N(SQ)
		 */
		sop->so_rcvconn = bp->bgn_nsq;

	} else {
		/*
		 * If retransmitted BGN, reject it
		 */
		if (sscop_is_rexmit(sop, bp->bgn_nsq)) {
			KB_FREEALL(m);
			sscop_send_bgrej(sop);
			return;
		}
	}

	if (sop->so_vers == SSCOP_VERS_QSAAL) {
		/*
		 * Get Source value
		 */
		if (bp->bgn_type & PT_SOURCE_SSCOP)
			source = SSCOP_SOURCE_SSCOP;
		else
			source = SSCOP_SOURCE_USER;

		/*
		 * Reset receiver state variables
		 */
		qsaal1_reset_rcvr(sop);
	} else
		source = 0;

	/*
	 * Set initial transmit window
	 */
	SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr));

	/*
	 * Pass connection request up to user
	 */
	STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku,
		sop->so_connvc, (int)m, source, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Wait for user's response
	 */
	sop->so_state = SOS_INCONN;

	return;
}
Ejemplo n.º 22
0
/*
 * SSCOP Lower Stack Command Handler
 * 
 * This function will receive all of the stack commands issued from the 
 * layer above SSCOP (ie. using the SSCOP SAP).  The appropriate processing
 * function will be determined based on the received stack command and the 
 * current sscop control block state.
 *
 * Arguments:
 *	cmd	stack command code
 *	tok	session token
 *	arg1	command specific argument
 *	arg2	command specific argument
 *
 * Returns:
 *	none
 *
 */
void
sscop_lower(int cmd, void *tok, int arg1, int arg2)
{
	struct sscop	*sop = (struct sscop *)tok;
	void		(**stab) (struct sscop *, int, int);
	void		(*func) (struct sscop *, int, int);
	int		val;

	ATM_DEBUG5("sscop_lower: cmd=0x%x, sop=%p, state=%d, arg1=0x%x, arg2=0x%x\n",
		cmd, sop, sop->so_state, arg1, arg2);

	/*
	 * Validate stack command
	 */
	val = cmd & STKCMD_VAL_MASK;
	if (((u_int)cmd  < (u_int)SSCOP_CMD_MIN) ||
	    ((u_int)cmd  > (u_int)SSCOP_CMD_MAX) ||
	    ((stab = (sop->so_vers == SSCOP_VERS_QSAAL ? 
			sscop_qsaal_aatab[val] : 
			sscop_q2110_aatab[val])) == NULL)) {
		log(LOG_ERR, "sscop_lower: unknown cmd 0x%x, sop=%p\n",
			cmd, sop);
		return;
	}

	/*
	 * Validate sscop state
	 */
	if (sop->so_state > SOS_MAXSTATE) {
		log(LOG_ERR, "sscop_lower: invalid state sop=%p, state=%d\n",
			sop, sop->so_state);
		/*
		 * Release possible buffer
		 */
		if (sscop_buf1[val]) {
			if (arg1)
				KB_FREEALL((KBuffer *)arg1);
		}
		return;
	}

	/*
	 * Validate command/state combination
	 */
	func = stab[sop->so_state];
	if (func == NULL) {
		log(LOG_ERR, 
			"sscop_lower: invalid cmd/state: sop=%p, cmd=0x%x, state=%d\n",
			sop, cmd, sop->so_state);
		/*
		 * Release possible buffer
		 */
		if (sscop_buf1[val]) {
			if (arg1)
				KB_FREEALL((KBuffer *)arg1);
		}
		return;
	}

	/*
	 * Call event processing function
	 */
	(*func)(sop, arg1, arg2);

	return;
}
Ejemplo n.º 23
0
/*
 * Supply Strategy 1 Large Buffers to CP
 *
 * May be called in interrupt state.
 * Must be called with interrupts locked out.
 *
 * Arguments:
 *	fup		pointer to device unit structure
 *
 * Returns:
 *	none
 */
static void
fore_buf_supply_1l(Fore_unit *fup)
{
	H_buf_queue	*hbp;
	Buf_queue	*cqp;
	Buf_descr	*bdp;
	Buf_handle	*bhp;
	KBuffer		*m;
	int		nvcc, nbuf, i;

	/*
	 * Figure out how many buffers we should be giving to the CP.
	 * We're basing this calculation on the current number of open
	 * VCCs thru this device, with certain minimum and maximum values
	 * enforced.  This will then allow us to figure out how many more 
	 * buffers we need to supply to the CP.  This will be rounded up 
	 * to fill a supply queue entry.
	 */
	nvcc = MAX(fup->fu_open_vcc, BUF_MIN_VCC);
	nbuf = nvcc * 4 * RECV_MAX_SEGS;
	nbuf = MIN(nbuf, BUF1_LG_CPPOOL);
	nbuf -= fup->fu_buf1l_cnt;
	nbuf = roundup(nbuf, BUF1_LG_ENTSIZE);

	/*
	 * OK, now supply the buffers to the CP
	 */
	while (nbuf > 0) {

		/*
		 * Acquire a supply queue entry
		 */
		hbp = fup->fu_buf1l_tail;
		if (!((*hbp->hbq_status) & QSTAT_FREE))
			break;
		bdp = hbp->hbq_descr;

		/*
		 * Get a buffer for each descriptor in the queue entry
		 */
		for (i = 0; i < BUF1_LG_ENTSIZE; i++, bdp++) {
			caddr_t		cp;

			/*
			 * Get a cluster buffer
			 */
			KB_ALLOCEXT(m, BUF1_LG_SIZE, KB_F_NOWAIT, KB_T_DATA);
			if (m == NULL) {
				break;
			}
			KB_HEADSET(m, BUF1_LG_DOFF);

			/*
			 * Point to buffer handle structure
			 */
			bhp = (Buf_handle *)((caddr_t)m + BUF1_LG_HOFF);
			bhp->bh_type = BHT_S1_LARGE;

			/*
			 * Setup buffer descriptor
			 */
			bdp->bsd_handle = bhp;
			KB_DATASTART(m, cp, caddr_t);
			bhp->bh_dma = bdp->bsd_buffer = (H_dma) DMA_GET_ADDR(
				cp, BUF1_LG_SIZE, BUF_DATA_ALIGN, 0);
			if (bdp->bsd_buffer == 0) {
				/*
				 * Unable to assign dma address - free up
				 * this descriptor's buffer
				 */
				fup->fu_stats->st_drv.drv_bf_segdma++;
				KB_FREEALL(m);
				break;
			}

			/*
			 * All set, so queue buffer (handle)
			 */
			ENQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1l_bq);
		}

		/*
		 * If we we're not able to fill all the descriptors for
		 * an entry, free up what's been partially built
		 */
		if (i != BUF1_LG_ENTSIZE) {
			caddr_t		cp;

			/*
			 * Clean up each used descriptor
			 */
			for (bdp = hbp->hbq_descr; i; i--, bdp++) {
				bhp = bdp->bsd_handle;

				DEQUEUE(bhp, Buf_handle, bh_qelem, 
					fup->fu_buf1l_bq);

				m = (KBuffer *)
					((caddr_t)bhp - BUF1_LG_HOFF);
				KB_DATASTART(m, cp, caddr_t);
				DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0);
				KB_FREEALL(m);
			}
			break;
		}

		/*
		 * Finally, we've got an entry ready for the CP.
		 * So claim the host queue entry and setup the CP-resident
		 * queue entry.  The CP will (potentially) grab the supplied
		 * buffers when the descriptor pointer is set.
		 */
		fup->fu_buf1l_tail = hbp->hbq_next;
		(*hbp->hbq_status) = QSTAT_PENDING;
		cqp = hbp->hbq_cpelem;
		cqp->cq_descr = (CP_dma) CP_WRITE((u_long)hbp->hbq_descr_dma);

		/*
		 * Update counters, etc for supplied buffers
		 */
		fup->fu_buf1l_cnt += BUF1_LG_ENTSIZE;
		nbuf -= BUF1_LG_ENTSIZE;
	}

	return;
}
Ejemplo n.º 24
0
/*
 * BGAK PDU / SOS_OUTCONN Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_bgak_outconn(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct bgak_pdu	*bp = (struct bgak_pdu *)trlr;
	int		err;

	/*
	 * Stop retransmit timer
	 */
	sop->so_timer[SSCOP_T_CC] = 0;

	/*
	 * Initialize transmit window
	 */
	SEQ_SET(sop->so_sendmax, ntohl(bp->bgak_nmr));

	/*
	 * Notify user of connection establishment
	 */
	if (sop->so_flags & SOF_REESTAB) {
		KB_FREEALL(m);
		STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku,
			sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err);
		if (err) {
			sscop_abort(sop, "stack memory\n");
			return;
		}
		sop->so_flags &= ~SOF_REESTAB;
	} else {
		STACK_CALL(SSCOP_ESTABLISH_CNF, sop->so_upper, sop->so_toku,
			sop->so_connvc, (int)m, 0, err);
		if (err) {
			KB_FREEALL(m);
			sscop_abort(sop, "stack memory\n");
			return;
		}
	}

	if (sop->so_vers == SSCOP_VERS_QSAAL) {
		/*
		 * Reset receiver variables
		 */
		qsaal1_reset_rcvr(sop);
	
		/*
		 * Start polling timer
		 */
		sscop_set_poll(sop);

		/*
		 * Start lost poll/stat timer
		 */
		sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;

	} else {
		/*
		 * Initialize state variables
		 */
		q2110_init_state(sop);

		/*
		 * Start data transfer timers
		 */
		sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
		sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
	}

	/*
	 * OK, we're ready for data
	 */
	sop->so_state = SOS_READY;

	/*
	 * See if transmit queues need servicing
	 */
	if (sop->so_flags & SOF_XMITSRVC)
		sscop_service_xmit(sop);

	return;
}
Ejemplo n.º 25
0
/*
 * BGN PDU / SOS_INRESYN Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_bgn_inresyn(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct bgn_pdu	*bp = (struct bgn_pdu *)trlr;
	int		err, source;

	/*
	 * If retransmitted BGN, oops
	 */
	if (sscop_is_rexmit(sop, bp->bgn_nsq)) {
		KB_FREEALL(m);
		sscop_maa_error(sop, 'B');
		return;
	}

	/*
	 * Stop data transfer timers
	 */
	sop->so_timer[SSCOP_T_POLL] = 0;
	sop->so_timer[SSCOP_T_NORESP] = 0;
	sop->so_timer[SSCOP_T_IDLE] = 0;
	sop->so_flags &= ~SOF_KEEPALIVE;

	/*
	 * Initialize transmit window
	 */
	SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr));

	if (sop->so_vers == SSCOP_VERS_QSAAL) {
		/*
		 * Get (possible) Source value
		 */
		if (bp->bgn_type & PT_SOURCE_SSCOP)
			source = SSCOP_SOURCE_SSCOP;
		else
			source = SSCOP_SOURCE_USER;

		/*
		 * Reset receiver variables
		 */
		qsaal1_reset_rcvr(sop);

	} else {
		/*
		 * Stop possible retransmit timer
		 */
		sop->so_timer[SSCOP_T_CC] = 0;

		/*
		 * Drain receiver queues
		 */
		sscop_rcvr_drain(sop);

		/*
		 * Tell user current connection has been released
		 */
		STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
			sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_USER, err);
		if (err) {
			KB_FREEALL(m);
			sscop_abort(sop, "stack memory\n");
			return;
		}

		source = 0;
	}

	/*
	 * Tell user of incoming connection
	 */
	STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku,
		sop->so_connvc, (int)m, source, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Wait for user's response
	 */
	sop->so_state = SOS_INCONN;

	return;
}
Ejemplo n.º 26
0
/*
 * BGN PDU / SOS_OUTRESYN Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_bgn_outresyn(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct bgn_pdu	*bp = (struct bgn_pdu *)trlr;
	int		err, source;

	/*
	 * If retransmitted BGN, ACK it and send new RS
	 */
	if (sscop_is_rexmit(sop, bp->bgn_nsq)) {
		KB_FREEALL(m);
		sscop_send_bgak(sop);
		sscop_send_rs(sop);
		return;
	}

	/*
	 * Stop retransmit timer
	 */
	sop->so_timer[SSCOP_T_CC] = 0;

	/*
	 * Initialize transmit window
	 */
	SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr));

	if (sop->so_vers == SSCOP_VERS_QSAAL) {
		/*
		 * Get (possible) Source value
		 */
		if (bp->bgn_type & PT_SOURCE_SSCOP)
			source = SSCOP_SOURCE_SSCOP;
		else
			source = SSCOP_SOURCE_USER;

		/*
		 * Reset receiver variables
		 */
		qsaal1_reset_rcvr(sop);
	
	} else
		source = SSCOP_SOURCE_USER;

	/*
	 * Notify user of connection termination
	 */
	STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
		sop->so_connvc, SSCOP_UU_NULL, source, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Now tell user of a "new" incoming connection
	 */
	STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku,
		sop->so_connvc, (int)m, source, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	/*
	 * Wait for user's response
	 */
	sop->so_state = SOS_INCONN;

	return;
}
Ejemplo n.º 27
0
/*
 * RSAK PDU / SOS_OUTRESYN Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_rsak_outresyn(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct rsak_q2110_pdu	*rp = (struct rsak_q2110_pdu *)trlr;
	int		err;

	/*
	 * Stop retransmit timer
	 */
	sop->so_timer[SSCOP_T_CC] = 0;

	/*
	 * Notify user of resynchronization completion
	 */
	STACK_CALL(SSCOP_RESYNC_CNF, sop->so_upper, sop->so_toku,
		sop->so_connvc, 0, 0, err);
	if (err) {
		KB_FREEALL(m);
		sscop_abort(sop, "stack memory\n");
		return;
	}

	if (sop->so_vers == SSCOP_VERS_QSAAL) {
		/*
		 * Start the polling timer
		 */
		sscop_set_poll(sop);

		/*
		 * Start lost poll/stat timer
		 */
		sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
	} else {
		/*
		 * Initialize state variables
		 */
		SEQ_SET(sop->so_sendmax, ntohl(rp->rsak_nmr));
		q2110_init_state(sop);

		/*
		 * Start data transfer timers
		 */     
		sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
		sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
	}

	/*
	 * Free buffers
	 */
	KB_FREEALL(m);

	/*
	 * Now go back to data transfer state
	 */
	sop->so_state = SOS_READY;

	/*
	 * See if transmit queues need servicing
	 */
	if (sop->so_flags & SOF_XMITSRVC)
		sscop_service_xmit(sop);

	return;
}
Ejemplo n.º 28
0
/*
 * USTAT PDU / SOS_READY Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_ustat_ready(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct ustat_pdu	*up = (struct ustat_pdu *)trlr;
	struct pdu_hdr	*php;
	sscop_seq	seq1, seq2;

	up->ustat_nmr = ntohl(up->ustat_nmr);
	up->ustat_nr = ntohl(up->ustat_nr);

	/*
	 * Validate peer's current receive data sequence number
	 */
	if (SEQ_GT(sop->so_ack, up->ustat_nr, sop->so_ack) ||
	    SEQ_GEQ(up->ustat_nr, sop->so_send, sop->so_ack)) {
		/*
		 * Bad data sequence number
		 */
		goto goterr;
	}

	/*
	 * Free acknowledged PDUs
	 */
	for (seq1 = sop->so_ack, SEQ_SET(seq2, up->ustat_nr);
			SEQ_LT(seq1, seq2, sop->so_ack);
			SEQ_INCR(seq1, 1)) {
		sscop_pack_free(sop, seq1);
	}

	/*
	 * Update transmit state variables
	 */
	sop->so_ack = seq2;
	SEQ_SET(sop->so_sendmax, up->ustat_nmr);

	/*
	 * Get USTAT list elements
	 */
	SEQ_SET(seq1, ntohl(up->ustat_le1));
	SEQ_SET(seq2, ntohl(up->ustat_le2));

	/*
	 * Validate elements
	 */
	if (SEQ_GT(sop->so_ack, seq1, sop->so_ack) ||
	    SEQ_GEQ(seq1, seq2, sop->so_ack) ||
	    SEQ_GEQ(seq2, sop->so_send, sop->so_ack)) {
		/*
		 * Bad element sequence number
		 */
		goto goterr;
	}

	/*
	 * Process each missing sequence number in this gap
	 */
	while (SEQ_LT(seq1, seq2, sop->so_ack)) {
		/*
		 * Find corresponding SD PDU on pending ack queue
		 */
		php = sscop_pack_locate(sop, seq1);
		if (php == NULL) {
			goto goterr;
		}

		/*
		 * Retransmit this SD PDU if it's not
		 * already scheduled for retranmission.
		 */
		if ((php->ph_rexmit_lk == NULL) &&
		    (sop->so_rexmit_tl != php)) {
			/*
			 * Put PDU on retransmit queue and schedule
			 * transmit servicing
			 */
			sscop_rexmit_insert(sop, php);
			sop->so_flags |= SOF_XMITSRVC;
		}

		/*
		 * Bump to next sequence number
		 */
		SEQ_INCR(seq1, 1);
	}

	/*
	 * Report retransmitted PDUs
	 */
	sscop_maa_error(sop, 'V');

	/*
	 * Free PDU buffer chain
	 */
	KB_FREEALL(m);

	/*
	 * See if transmit queues need servicing
	 */
	if (sop->so_flags & SOF_XMITSRVC)
		sscop_service_xmit(sop);

	return;

goterr:
	/*
	 * Protocol/parameter error encountered
	 */
	sscop_maa_error(sop, 'T');

	/*
	 * Free PDU buffer chain
	 */
	KB_FREEALL(m);

	if (sop->so_vers == SSCOP_VERS_QSAAL)
		/*
		 * Reestablish a new connection
		 */
		qsaal1_reestablish(sop);
	else
		/*
		 * Initiate error recovery
		 */
		q2110_error_recovery(sop);

	return;
}
Ejemplo n.º 29
0
/*
 * Free Buffer Supply Queue Data Structures
 *
 * Arguments:
 *	fup		pointer to device unit structure
 *
 * Returns:
 *	none
 */
void
fore_buf_free(Fore_unit *fup)
{
	Buf_handle	*bhp;
	KBuffer		*m;

	/*
	 * Free any previously supplied and not returned buffers
	 */
	if (fup->fu_flags & CUF_INITED) {

		/*
		 * Run through Strategy 1 Small queue
		 */
		while ((bhp = Q_HEAD(fup->fu_buf1s_bq, Buf_handle)) != NULL) {
			caddr_t		cp;

			/*
			 * Back off to buffer
			 */
			m = (KBuffer *)((caddr_t)bhp - BUF1_SM_HOFF);

			/*
			 * Dequeue handle and free buffer
			 */
			DEQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1s_bq);

			KB_DATASTART(m, cp, caddr_t);
			DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_SM_SIZE, 0);

			KB_FREEALL(m);
		}

		/*
		 * Run through Strategy 1 Large queue
		 */
		while ((bhp = Q_HEAD(fup->fu_buf1l_bq, Buf_handle)) != NULL) {
			caddr_t		cp;

			/*
			 * Back off to buffer
			 */
			m = (KBuffer *)((caddr_t)bhp - BUF1_LG_HOFF);

			/*
			 * Dequeue handle and free buffer
			 */
			DEQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1l_bq);

			KB_DATASTART(m, cp, caddr_t);
			DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0);

			KB_FREEALL(m);
		}
	}

	/*
	 * Free the status words
	 */
	if (fup->fu_buf1s_stat) {
		if (fup->fu_buf1s_statd) {
			DMA_FREE_ADDR(fup->fu_buf1s_stat, fup->fu_buf1s_statd,
				sizeof(Q_status) *
					(BUF1_SM_QUELEN + BUF1_LG_QUELEN),
				ATM_DEV_NONCACHE);
		}
		atm_dev_free((volatile void *)fup->fu_buf1s_stat);
		fup->fu_buf1s_stat = NULL;
		fup->fu_buf1s_statd = NULL;
		fup->fu_buf1l_stat = NULL;
		fup->fu_buf1l_statd = NULL;
	}

	/*
	 * Free the transmit descriptors
	 */
	if (fup->fu_buf1s_desc) {
		if (fup->fu_buf1s_descd) {
			DMA_FREE_ADDR(fup->fu_buf1s_desc, fup->fu_buf1s_descd,
				sizeof(Buf_descr) *
					((BUF1_SM_QUELEN * BUF1_SM_ENTSIZE) +
					 (BUF1_LG_QUELEN * BUF1_LG_ENTSIZE)),
				0);
		}
		atm_dev_free(fup->fu_buf1s_desc);
		fup->fu_buf1s_desc = NULL;
		fup->fu_buf1s_descd = NULL;
		fup->fu_buf1l_desc = NULL;
		fup->fu_buf1l_descd = NULL;
	}

	return;
}
Ejemplo n.º 30
0
/*
 * STAT PDU / SOS_READY Processor
 *
 * Arguments:
 *	sop	pointer to sscop connection block
 *	m	pointer to PDU buffer (without trailer)
 *	trlr	pointer to PDU trailer
 *
 * Returns:
 *	none
 *
 */
void
sscop_stat_ready(struct sscop *sop, KBuffer *m, caddr_t trlr)
{
	struct stat_pdu	*sp = (struct stat_pdu *)trlr;
	struct pdu_hdr	*php;
	KBuffer		*m0 = m;
	sscop_seq	seq1, seq2, opa;
	int		cnt = 0;

	sp->stat_nps = ntohl(sp->stat_nps);
	sp->stat_nmr = ntohl(sp->stat_nmr);
	sp->stat_nr = ntohl(sp->stat_nr);

	/*
	 * Validate peer's received poll sequence number
	 */
	if (SEQ_GT(sop->so_pollack, sp->stat_nps, sop->so_pollack) ||
	    SEQ_GT(sp->stat_nps, sop->so_pollsend, sop->so_pollack)) {
		/*
		 * Bad poll sequence number
		 */
		sscop_maa_error(sop, 'R');
		goto goterr;
	}

	/*
	 * Validate peer's current receive data sequence number
	 */
	if (SEQ_GT(sop->so_ack, sp->stat_nr, sop->so_ack) ||
	    SEQ_GT(sp->stat_nr, sop->so_send, sop->so_ack)) {
		/*
		 * Bad data sequence number
		 */
		sscop_maa_error(sop, 'S');
		goto goterr;
	}

	/*
	 * Free acknowledged PDUs
	 */
	for (seq1 = sop->so_ack, SEQ_SET(seq2, sp->stat_nr);
			SEQ_LT(seq1, seq2, sop->so_ack);
			SEQ_INCR(seq1, 1)) {
		sscop_pack_free(sop, seq1);
	}

	/*
	 * Update transmit state variables
	 */
	opa = sop->so_pollack;
	sop->so_ack = seq2;
	SEQ_SET(sop->so_pollack, sp->stat_nps);
	SEQ_SET(sop->so_sendmax, sp->stat_nmr);

	/*
	 * Get first element in STAT list
	 */
	while (m && (KB_LEN(m) == 0))
		m = KB_NEXT(m);
	if (m == NULL)
		goto done;
	m = sscop_stat_getelem(m, &seq1);

	/*
	 * Make sure there's a second element too
	 */
	if (m == NULL)
		goto done;

	/*
	 * Validate first element (start of missing pdus)
	 */
	if (SEQ_GT(sop->so_ack, seq1, sop->so_ack) ||
	    SEQ_GEQ(seq1, sop->so_send, sop->so_ack)) {
		/*
		 * Bad element sequence number
		 */
		sscop_maa_error(sop, 'S');
		goto goterr;
	}

	/*
	 * Loop thru all STAT elements in list
	 */
	while (m) {
		/*
		 * Get next even element (start of received pdus)
		 */
		m = sscop_stat_getelem(m, &seq2);

		/*
		 * Validate seqence number
		 */
		if (SEQ_GEQ(seq1, seq2, sop->so_ack) ||
		    SEQ_GT(seq2, sop->so_send, sop->so_ack)) {
			/*
			 * Bad element sequence number
			 */
			sscop_maa_error(sop, 'S');
			goto goterr;
		}

		/*
		 * Process each missing sequence number in this gap
		 */
		while (SEQ_LT(seq1, seq2, sop->so_ack)) {
			/*
			 * Find corresponding SD PDU on pending ack queue
			 */
			php = sscop_pack_locate(sop, seq1);
			if (php == NULL) {
				sscop_maa_error(sop, 'S');
				goto goterr;
			}

			/*
			 * Retransmit this SD PDU only if it was last sent
			 * during an earlier poll sequence and it's not
			 * already scheduled for retranmission.
			 */
			if (SEQ_LT(php->ph_nps, sp->stat_nps, opa) &&
			    (php->ph_rexmit_lk == NULL) &&
			    (sop->so_rexmit_tl != php)) {
				/*
				 * Put PDU on retransmit queue and schedule
				 * transmit servicing
				 */
				sscop_rexmit_insert(sop, php);
				sop->so_flags |= SOF_XMITSRVC;
				cnt++;
			}

			/*
			 * Bump to next sequence number
			 */
			SEQ_INCR(seq1, 1);
		}

		/*
		 * Now process series of acknowledged PDUs
		 *
		 * Get next odd element (start of missing pdus),
		 * but make sure there is one and that it's valid
		 */
		if (m == NULL)
			goto done;
		m = sscop_stat_getelem(m, &seq2);
		if (SEQ_GEQ(seq1, seq2, sop->so_ack) ||
		    SEQ_GT(seq2, sop->so_send, sop->so_ack)) {
			/*
			 * Bad element sequence number
			 */
			sscop_maa_error(sop, 'S');
			goto goterr;
		}

		/*
		 * Process each acked sequence number
		 */
		while (SEQ_LT(seq1, seq2, sop->so_ack)) {
			/*
			 * Can we clear transmit buffers ??
			 */
			if ((sop->so_flags & SOF_NOCLRBUF) == 0) {
				/*
				 * Yes, free acked buffers
				 */
				sscop_pack_free(sop, seq1);
			}

			/*
			 * Bump to next sequence number
			 */
			SEQ_INCR(seq1, 1);
		}
	}

done:
	/*
	 * Free PDU buffer chain
	 */
	KB_FREEALL(m0);

	/*
	 * Report retransmitted PDUs
	 */
	if (cnt)
		sscop_maa_error(sop, 'V');

	/*
	 * Record transmit window closed transitions
	 */
	if (SEQ_LT(sop->so_send, sop->so_sendmax, sop->so_ack)) {
		if (sop->so_flags & SOF_NOCREDIT) {
			sop->so_flags &= ~SOF_NOCREDIT;
			sscop_maa_error(sop, 'X');
		}
	} else {
		if ((sop->so_flags & SOF_NOCREDIT) == 0) {
			sop->so_flags |= SOF_NOCREDIT;
			sscop_maa_error(sop, 'W');
		}
	}

	if (sop->so_vers == SSCOP_VERS_QSAAL)
		/*
		 * Restart lost poll/stat timer
		 */
		sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
	else {
		/*
		 * Determine new polling phase
		 */
		if ((sop->so_timer[SSCOP_T_POLL] != 0) &&
		    ((sop->so_flags & SOF_KEEPALIVE) == 0)) {
			/*
			 * Remain in active phase - reset NO-RESPONSE timer
			 */
			sop->so_timer[SSCOP_T_NORESP] =
					sop->so_parm.sp_timeresp;

		} else if (sop->so_timer[SSCOP_T_IDLE] == 0) {
			/*
			 * Go from transient to idle phase
			 */
			sop->so_timer[SSCOP_T_POLL] = 0;
			sop->so_flags &= ~SOF_KEEPALIVE;
			sop->so_timer[SSCOP_T_NORESP] = 0;
			sop->so_timer[SSCOP_T_IDLE] = sop->so_parm.sp_timeidle;
		}
	}

	/*
	 * See if transmit queues need servicing
	 */
	if (sop->so_flags & SOF_XMITSRVC)
		sscop_service_xmit(sop);

	return;

goterr:
	/*
	 * Protocol/parameter error encountered
	 */

	/*
	 * Free PDU buffer chain
	 */
	KB_FREEALL(m0);

	if (sop->so_vers == SSCOP_VERS_QSAAL)
		/*
		 * Reestablish a new connection
		 */
		qsaal1_reestablish(sop);
	else
		/*
		 * Initiate error recovery
		 */
		q2110_error_recovery(sop);

	return;
}