summaryrefslogtreecommitdiff
path: root/sys/netiso/tp_driver.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sys/netiso/tp_driver.c
initial import of NetBSD tree
Diffstat (limited to 'sys/netiso/tp_driver.c')
-rw-r--r--sys/netiso/tp_driver.c997
1 files changed, 997 insertions, 0 deletions
diff --git a/sys/netiso/tp_driver.c b/sys/netiso/tp_driver.c
new file mode 100644
index 00000000000..0a781abcfa0
--- /dev/null
+++ b/sys/netiso/tp_driver.c
@@ -0,0 +1,997 @@
+/* $NetBSD: tp_driver.c,v 1.6 1994/06/29 06:40:01 cgd Exp $ */
+
+#define _XEBEC_PG static
+
+#include "tp_states.h"
+
+static struct act_ent {
+ int a_newstate;
+ int a_action;
+} statetable[] = { {0,0},
+#include "tp_states.init"
+};
+
+/* @(#)tp.trans 8.1 (Berkeley) 6/10/93 */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/mbuf.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+
+#include <netiso/tp_param.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_trace.h>
+#include <netiso/iso_errno.h>
+#include <netiso/tp_seq.h>
+#include <netiso/cons.h>
+
+#define DRIVERTRACE TPPTdriver
+#define sbwakeup(sb) sowakeup(p->tp_sock, sb);
+#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0)
+
+static trick_hc = 1;
+
+int tp_emit(),
+ tp_goodack(), tp_goodXack(),
+ tp_stash()
+;
+void tp_indicate(), tp_getoptions(),
+ tp_soisdisconnecting(), tp_soisdisconnected(),
+ tp_recycle_tsuffix(),
+#ifdef TP_DEBUG_TIMERS
+ tp_etimeout(), tp_euntimeout(),
+ tp_ctimeout(), tp_cuntimeout(),
+ tp_ctimeout_MIN(),
+#endif
+ tp_freeref(), tp_detach(),
+ tp0_stash(), tp0_send(),
+ tp_netcmd(), tp_send()
+;
+
+typedef struct tp_pcb tpcb_struct;
+
+
+
+typedef tpcb_struct tp_PCB_;
+
+#include "tp_events.h"
+
+_XEBEC_PG int _Xebec_action(a,e,p)
+int a;
+struct tp_event *e;
+tp_PCB_ *p;
+{
+switch(a) {
+case -1: return tp_protocol_error(e,p);
+case 0x1:
+ {
+ (void) tp_emit(DC_TPDU_type, p, 0, 0, MNULL);
+ }
+ break;
+case 0x2:
+ {
+# ifdef TP_DEBUG
+ if( e->ev_number != AK_TPDU )
+ printf("TPDU 0x%x in REFWAIT!!!!\n", e->ev_number);
+# endif TP_DEBUG
+ }
+ break;
+case 0x3:
+ {
+ /* oh, man is this grotesque or what? */
+ (void) tp_goodack(p, e->ev_union.EV_AK_TPDU.e_cdt, e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_subseq);
+ /* but it's necessary because this pseudo-ack may happen
+ * before the CC arrives, but we HAVE to adjust the
+ * snduna as a result of the ack, WHENEVER it arrives
+ */
+ }
+ break;
+case 0x4:
+ {
+ tp_detach(p);
+ }
+ break;
+case 0x5:
+ {
+ p->tp_refstate = REF_OPEN; /* has timers ??? */
+ }
+ break;
+case 0x6:
+ {
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "CR datalen data", e->ev_union.EV_CR_TPDU.e_datalen, e->ev_union.EV_CR_TPDU.e_data,0,0);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("CR datalen 0x%x data 0x%x", e->ev_union.EV_CR_TPDU.e_datalen, e->ev_union.EV_CR_TPDU.e_data);
+ ENDDEBUG
+ p->tp_refstate = REF_OPEN; /* has timers */
+ p->tp_fcredit = e->ev_union.EV_CR_TPDU.e_cdt;
+
+ if (e->ev_union.EV_CR_TPDU.e_datalen > 0) {
+ /* n/a for class 0 */
+ ASSERT(p->tp_Xrcv.sb_cc == 0);
+ sbappendrecord(&p->tp_Xrcv, e->ev_union.EV_CR_TPDU.e_data);
+ e->ev_union.EV_CR_TPDU.e_data = MNULL;
+ }
+ }
+ break;
+case 0x7:
+ {
+ IncStat(ts_tp0_conn);
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "Confiming", p, 0,0,0);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("Confirming connection: p" );
+ ENDDEBUG
+ soisconnected(p->tp_sock);
+ (void) tp_emit(CC_TPDU_type, p, 0,0, MNULL) ;
+ p->tp_fcredit = 1;
+ }
+ break;
+case 0x8:
+ {
+ IncStat(ts_tp4_conn); /* even though not quite open */
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "Confiming", p, 0,0,0);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("Confirming connection: p" );
+ ENDDEBUG
+ tp_getoptions(p);
+ soisconnecting(p->tp_sock);
+ if ((p->tp_rx_strat & TPRX_FASTSTART) && (p->tp_fcredit > 0))
+ p->tp_cong_win = p->tp_fcredit * p->tp_l_tpdusize;
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_cc_ticks);
+ }
+ break;
+case 0x9:
+ {
+ IFDEBUG(D_CONN)
+ printf("event: CR_TPDU emit CC failed done " );
+ ENDDEBUG
+ soisdisconnected(p->tp_sock);
+ tp_recycle_tsuffix(p);
+ tp_freeref(p->tp_lref);
+ tp_detach(p);
+ }
+ break;
+case 0xa:
+ {
+ int error;
+ struct mbuf *data = MNULL;
+
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)p->tp_flags,
+ p->tp_ucddata, 0, 0);
+ ENDTRACE
+ data = MCPY(p->tp_ucddata, M_WAIT);
+ if (data) {
+ IFDEBUG(D_CONN)
+ printf("T_CONN_req.trans m_copy cc 0x%x\n",
+ p->tp_ucddata);
+ dump_mbuf(data, "sosnd @ T_CONN_req");
+ ENDDEBUG
+ }
+
+ if (error = tp_emit(CR_TPDU_type, p, 0, 0, data) )
+ return error; /* driver WON'T change state; will return error */
+
+ p->tp_refstate = REF_OPEN; /* has timers */
+ if(p->tp_class != TP_CLASS_0) {
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_cr_ticks);
+ }
+ }
+ break;
+case 0xb:
+ {
+ sbflush(&p->tp_Xrcv); /* purge non-delivered data data */
+ if (e->ev_union.EV_DR_TPDU.e_datalen > 0) {
+ sbappendrecord(&p->tp_Xrcv, e->ev_union.EV_DR_TPDU.e_data);
+ e->ev_union.EV_DR_TPDU.e_data = MNULL;
+ }
+ if (p->tp_state == TP_OPEN)
+ tp_indicate(T_DISCONNECT, p, 0);
+ else {
+ int so_error = ECONNREFUSED;
+ if (e->ev_union.EV_DR_TPDU.e_reason != (E_TP_NO_SESSION ^ TP_ERROR_MASK) &&
+ e->ev_union.EV_DR_TPDU.e_reason != (E_TP_NO_CR_ON_NC ^ TP_ERROR_MASK) &&
+ e->ev_union.EV_DR_TPDU.e_reason != (E_TP_REF_OVERFLOW ^ TP_ERROR_MASK))
+ so_error = ECONNABORTED;
+ tp_indicate(T_DISCONNECT, p, so_error);
+ }
+ tp_soisdisconnected(p);
+ if (p->tp_class != TP_CLASS_0) {
+ if (p->tp_state == TP_OPEN ) {
+ tp_euntimeout(p, TM_data_retrans); /* all */
+ tp_cuntimeout(p, TM_retrans);
+ tp_cuntimeout(p, TM_inact);
+ tp_cuntimeout(p, TM_sendack);
+ p->tp_flags &= ~TPF_DELACK;
+ }
+ tp_cuntimeout(p, TM_retrans);
+ if( e->ev_union.EV_DR_TPDU.e_sref != 0 )
+ (void) tp_emit(DC_TPDU_type, p, 0, 0, MNULL);
+ }
+ }
+ break;
+case 0xc:
+ {
+ if( e->ev_union.EV_DR_TPDU.e_sref != 0 )
+ (void) tp_emit(DC_TPDU_type, p, 0, 0, MNULL);
+ /* reference timer already set - reset it to be safe (???) */
+ tp_euntimeout(p, TM_reference); /* all */
+ tp_etimeout(p, TM_reference, (int)p->tp_refer_ticks);
+ }
+ break;
+case 0xd:
+ {
+ tp_cuntimeout(p, TM_retrans);
+ tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0xe:
+ {
+ tp_cuntimeout(p, TM_retrans);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0xf:
+ {
+ tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason);
+ tp_cuntimeout(p, TM_retrans);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0x10:
+ {
+ tp_cuntimeout(p, TM_retrans);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0x11:
+ { /* don't ask me why we have to do this - spec says so */
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_NO_SESSION, MNULL);
+ /* don't bother with retransmissions of the DR */
+ }
+ break;
+case 0x12:
+ {
+ tp_soisdisconnecting(p->tp_sock);
+ tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason);
+ tp_soisdisconnected(p);
+ tp_netcmd( p, CONN_CLOSE );
+ }
+ break;
+case 0x13:
+ {
+ if (p->tp_state == TP_OPEN) {
+ tp_euntimeout(p, TM_data_retrans); /* all */
+ tp_cuntimeout(p, TM_inact);
+ tp_cuntimeout(p, TM_sendack);
+ }
+ tp_soisdisconnecting(p->tp_sock);
+ tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason);
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_PROTO_ERR, MNULL);
+ }
+ break;
+case 0x14:
+ {
+ tp_cuntimeout(p, TM_retrans);
+ IncStat(ts_tp0_conn);
+ p->tp_fcredit = 1;
+ soisconnected(p->tp_sock);
+ }
+ break;
+case 0x15:
+ {
+ IFDEBUG(D_CONN)
+ printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
+ (int)p->tp_flags);
+ ENDDEBUG
+ IncStat(ts_tp4_conn);
+ p->tp_fref = e->ev_union.EV_CC_TPDU.e_sref;
+ p->tp_fcredit = e->ev_union.EV_CC_TPDU.e_cdt;
+ if ((p->tp_rx_strat & TPRX_FASTSTART) && (e->ev_union.EV_CC_TPDU.e_cdt > 0))
+ p->tp_cong_win = e->ev_union.EV_CC_TPDU.e_cdt * p->tp_l_tpdusize;
+ tp_getoptions(p);
+ tp_cuntimeout(p, TM_retrans);
+ if (p->tp_ucddata) {
+ IFDEBUG(D_CONN)
+ printf("dropping user connect data cc 0x%x\n",
+ p->tp_ucddata->m_len);
+ ENDDEBUG
+ m_freem(p->tp_ucddata);
+ p->tp_ucddata = 0;
+ }
+ soisconnected(p->tp_sock);
+ if (e->ev_union.EV_CC_TPDU.e_datalen > 0) {
+ ASSERT(p->tp_Xrcv.sb_cc == 0); /* should be empty */
+ sbappendrecord(&p->tp_Xrcv, e->ev_union.EV_CC_TPDU.e_data);
+ e->ev_union.EV_CC_TPDU.e_data = MNULL;
+ }
+
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ break;
+case 0x16:
+ {
+ struct mbuf *data = MNULL;
+ int error;
+
+ IncStat(ts_retrans_cr);
+ p->tp_cong_win = 1 * p->tp_l_tpdusize;
+ data = MCPY(p->tp_ucddata, M_NOWAIT);
+ if(p->tp_ucddata) {
+ IFDEBUG(D_CONN)
+ printf("TM_retrans.trans m_copy cc 0x%x\n", data);
+ dump_mbuf(p->tp_ucddata, "sosnd @ TM_retrans");
+ ENDDEBUG
+ if( data == MNULL )
+ return ENOBUFS;
+ }
+
+ p->tp_retrans --;
+ if( error = tp_emit(CR_TPDU_type, p, 0, 0, data) ) {
+ p->tp_sock->so_error = error;
+ }
+ tp_ctimeout(p, TM_retrans, (int)p->tp_cr_ticks);
+ }
+ break;
+case 0x17:
+ {
+ IncStat(ts_conn_gaveup);
+ p->tp_sock->so_error = ETIMEDOUT;
+ tp_indicate(T_DISCONNECT, p, ETIMEDOUT);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0x18:
+ {
+ int error;
+ struct mbuf *data = MCPY(p->tp_ucddata, M_WAIT);
+
+ if( error = tp_emit(CC_TPDU_type, p, 0, 0, data) ) {
+ p->tp_sock->so_error = error;
+ }
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_cc_ticks);
+ }
+ break;
+case 0x19:
+ {
+ int doack;
+
+ /*
+ * Get rid of any confirm or connect data, so that if we
+ * crash or close, it isn't thought of as disconnect data.
+ */
+ if (p->tp_ucddata) {
+ m_freem(p->tp_ucddata);
+ p->tp_ucddata = 0;
+ }
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ tp_cuntimeout(p, TM_retrans);
+ soisconnected(p->tp_sock);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+
+ /* see also next 2 transitions, if you make any changes */
+
+ doack = tp_stash(p, e);
+ IFDEBUG(D_DATA)
+ printf("tp_stash returns %d\n",doack);
+ ENDDEBUG
+
+ if (doack) {
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL );
+ tp_ctimeout(p, TM_sendack, (int)p->tp_keepalive_ticks);
+ } else
+ tp_ctimeout( p, TM_sendack, (int)p->tp_sendack_ticks);
+
+ IFDEBUG(D_DATA)
+ printf("after stash calling sbwakeup\n");
+ ENDDEBUG
+ }
+ break;
+case 0x1a:
+ {
+ tp0_stash(p, e);
+ sbwakeup( &p->tp_sock->so_rcv );
+
+ IFDEBUG(D_DATA)
+ printf("after stash calling sbwakeup\n");
+ ENDDEBUG
+ }
+ break;
+case 0x1b:
+ {
+ int doack; /* tells if we must ack immediately */
+
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ sbwakeup( &p->tp_sock->so_rcv );
+
+ doack = tp_stash(p, e);
+ IFDEBUG(D_DATA)
+ printf("tp_stash returns %d\n",doack);
+ ENDDEBUG
+
+ if(doack)
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL );
+ else
+ tp_ctimeout_MIN( p, TM_sendack, (int)p->tp_sendack_ticks);
+
+ IFDEBUG(D_DATA)
+ printf("after stash calling sbwakeup\n");
+ ENDDEBUG
+ }
+ break;
+case 0x1c:
+ {
+ IFTRACE(D_DATA)
+ tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
+ e->ev_union.EV_DT_TPDU.e_seq, p->tp_rcvnxt, p->tp_lcredit, 0);
+ ENDTRACE
+ IncStat(ts_dt_niw);
+ m_freem(e->ev_union.EV_DT_TPDU.e_data);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL );
+ }
+ break;
+case 0x1d:
+ {
+ if (p->tp_ucddata) {
+ m_freem(p->tp_ucddata);
+ p->tp_ucddata = 0;
+ }
+ (void) tp_goodack(p, e->ev_union.EV_AK_TPDU.e_cdt, e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_subseq);
+ tp_cuntimeout(p, TM_retrans);
+
+ soisconnected(p->tp_sock);
+ IFTRACE(D_CONN)
+ struct socket *so = p->tp_sock;
+ tptrace(TPPTmisc,
+ "called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
+ so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
+ tptrace(TPPTmisc,
+ "called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
+ so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
+ ENDTRACE
+
+ tp_ctimeout(p, TM_sendack, (int)p->tp_keepalive_ticks);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ break;
+case 0x1e:
+ {
+ if( p->tp_state == TP_AKWAIT ) {
+ if (p->tp_ucddata) {
+ m_freem(p->tp_ucddata);
+ p->tp_ucddata = 0;
+ }
+ tp_cuntimeout(p, TM_retrans);
+ soisconnected(p->tp_sock);
+ tp_ctimeout(p, TM_sendack, (int)p->tp_keepalive_ticks);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
+ p->tp_Xrcvnxt,e->ev_union.EV_XPD_TPDU.e_seq, e->ev_union.EV_XPD_TPDU.e_datalen, e->ev_union.EV_XPD_TPDU.e_data->m_len);
+ ENDTRACE
+
+ p->tp_sock->so_state |= SS_RCVATMARK;
+ e->ev_union.EV_XPD_TPDU.e_data->m_flags |= M_EOR;
+ sbinsertoob(&p->tp_Xrcv, e->ev_union.EV_XPD_TPDU.e_data);
+ IFDEBUG(D_XPD)
+ dump_mbuf(e->ev_union.EV_XPD_TPDU.e_data, "XPD TPDU: tp_Xrcv");
+ ENDDEBUG
+ tp_indicate(T_XDATA, p, 0);
+ sbwakeup( &p->tp_Xrcv );
+
+ (void) tp_emit(XAK_TPDU_type, p, p->tp_Xrcvnxt, 0, MNULL);
+ SEQ_INC(p, p->tp_Xrcvnxt);
+ }
+ break;
+case 0x1f:
+ {
+ if( p->tp_Xrcv.sb_cc == 0 ) {
+ /* kludge for select(): */
+ /* p->tp_sock->so_state &= ~SS_OOBAVAIL; */
+ }
+ }
+ break;
+case 0x20:
+ {
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
+ p->tp_Xrcvnxt, e->ev_union.EV_XPD_TPDU.e_seq, p->tp_Xrcv.sb_cc , 0);
+ ENDTRACE
+ if( p->tp_Xrcvnxt != e->ev_union.EV_XPD_TPDU.e_seq )
+ IncStat(ts_xpd_niw);
+ if( p->tp_Xrcv.sb_cc ) {
+ /* might as well kick 'em again */
+ tp_indicate(T_XDATA, p, 0);
+ IncStat(ts_xpd_dup);
+ }
+ m_freem(e->ev_union.EV_XPD_TPDU.e_data);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ /* don't send an xack because the xak gives "last one received", not
+ * "next one i expect" (dumb)
+ */
+ }
+ break;
+case 0x21:
+ {
+ struct socket *so = p->tp_sock;
+
+ /* detach from parent socket so it can finish closing */
+ if (so->so_head) {
+ if (!soqremque(so, 0) && !soqremque(so, 1))
+ panic("tp: T_DETACH");
+ so->so_head = 0;
+ }
+ tp_soisdisconnecting(p->tp_sock);
+ tp_netcmd( p, CONN_CLOSE);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0x22:
+ {
+ struct socket *so = p->tp_sock;
+ struct mbuf *data = MNULL;
+
+ /* detach from parent socket so it can finish closing */
+ if (so->so_head) {
+ if (!soqremque(so, 0) && !soqremque(so, 1))
+ panic("tp: T_DETACH");
+ so->so_head = 0;
+ }
+ if (p->tp_state != TP_CLOSING) {
+ tp_soisdisconnecting(p->tp_sock);
+ data = MCPY(p->tp_ucddata, M_NOWAIT);
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_NORMAL_DISC, data);
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+ }
+ }
+ break;
+case 0x23:
+ {
+ tp_soisdisconnecting(p->tp_sock);
+ tp_netcmd( p, CONN_CLOSE);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0x24:
+ {
+ struct mbuf *data = MCPY(p->tp_ucddata, M_WAIT);
+
+ if(p->tp_state == TP_OPEN) {
+ tp_euntimeout(p, TM_data_retrans); /* all */
+ tp_cuntimeout(p, TM_inact);
+ tp_cuntimeout(p, TM_sendack);
+ p->tp_flags &= ~TPF_DELACK;
+ }
+ if (data) {
+ IFDEBUG(D_CONN)
+ printf("T_DISC_req.trans tp_ucddata 0x%x\n",
+ p->tp_ucddata);
+ dump_mbuf(data, "ucddata @ T_DISC_req");
+ ENDDEBUG
+ }
+ tp_soisdisconnecting(p->tp_sock);
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+
+ if( trick_hc )
+ return tp_emit(DR_TPDU_type, p, 0, e->ev_union.EV_T_DISC_req.e_reason, data);
+ }
+ break;
+case 0x25:
+ {
+ int error;
+ struct mbuf *data = MCPY(p->tp_ucddata, M_WAIT);
+
+ IncStat(ts_retrans_cc);
+ p->tp_retrans --;
+ p->tp_cong_win = 1 * p->tp_l_tpdusize;
+
+ if( error = tp_emit(CC_TPDU_type, p, 0, 0, data) )
+ p->tp_sock->so_error = error;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_cc_ticks);
+ }
+ break;
+case 0x26:
+ {
+ IncStat(ts_conn_gaveup);
+ tp_soisdisconnecting(p->tp_sock);
+ p->tp_sock->so_error = ETIMEDOUT;
+ tp_indicate(T_DISCONNECT, p, ETIMEDOUT);
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_CONGEST, MNULL);
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+ }
+ break;
+case 0x27:
+ {
+ tp_euntimeout(p, TM_data_retrans); /* all */
+ tp_cuntimeout(p, TM_inact);
+ tp_cuntimeout(p, TM_sendack);
+
+ IncStat(ts_conn_gaveup);
+ tp_soisdisconnecting(p->tp_sock);
+ p->tp_sock->so_error = ETIMEDOUT;
+ tp_indicate(T_DISCONNECT, p, ETIMEDOUT);
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_CONGEST_2, MNULL);
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+ }
+ break;
+case 0x28:
+ {
+ p->tp_cong_win = 1 * p->tp_l_tpdusize;
+ /* resume XPD */
+ if ( p->tp_Xsnd.sb_mb ) {
+ struct mbuf *m = m_copy(p->tp_Xsnd.sb_mb, 0, (int)p->tp_Xsnd.sb_cc);
+ int shift;
+
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna",
+ p->tp_Xuna, p->tp_Xsndnxt, p->tp_sndnxt,
+ p->tp_snduna);
+ ENDTRACE
+ IFDEBUG(D_XPD)
+ dump_mbuf(m, "XPD retrans emitting M");
+ ENDDEBUG
+ IncStat(ts_retrans_xpd);
+ p->tp_retrans --;
+ shift = max(p->tp_Nretrans - p->tp_retrans, 6);
+ (void) tp_emit(XPD_TPDU_type, p, p->tp_Xuna, 1, m);
+ tp_ctimeout(p, TM_retrans, ((int)p->tp_dt_ticks) << shift);
+ }
+ }
+ break;
+case 0x29:
+ {
+ p->tp_rxtshift++;
+ (void) tp_data_retrans(p);
+ }
+ break;
+case 0x2a:
+ {
+ p->tp_retrans --;
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_DR_NO_REAS, MNULL);
+ IncStat(ts_retrans_dr);
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+ }
+ break;
+case 0x2b:
+ {
+ p->tp_sock->so_error = ETIMEDOUT;
+ p->tp_refstate = REF_FROZEN;
+ tp_recycle_tsuffix( p );
+ tp_etimeout(p, TM_reference, (int)p->tp_refer_ticks);
+ }
+ break;
+case 0x2c:
+ {
+ tp_freeref(p->tp_lref);
+ tp_detach(p);
+ }
+ break;
+case 0x2d:
+ {
+ if( p->tp_class != TP_CLASS_0) {
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ if ( e->ev_number == CC_TPDU )
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL);
+ }
+ /* ignore it if class 0 - state tables are blank for this */
+ }
+ break;
+case 0x2e:
+ {
+ IFTRACE(D_DATA)
+ tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb",
+ p->tp_sndnxt, p->tp_snduna, p->tp_fcredit, p);
+ ENDTRACE
+
+ tp_send(p);
+ }
+ break;
+case 0x2f:
+ {
+ int error = 0;
+
+ /* resume XPD */
+ if ( p->tp_Xsnd.sb_mb ) {
+ struct mbuf *m = m_copy(p->tp_Xsnd.sb_mb, 0, (int)p->tp_Xsnd.sb_cc);
+ /* m_copy doesn't preserve the m_xlink field, but at this pt.
+ * that doesn't matter
+ */
+
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna",
+ p->tp_Xuna, p->tp_Xsndnxt, p->tp_sndnxt,
+ p->tp_snduna);
+ ENDTRACE
+ IFDEBUG(D_XPD)
+ printf("T_XPD_req: sb_cc 0x%x\n", p->tp_Xsnd.sb_cc);
+ dump_mbuf(m, "XPD req emitting M");
+ ENDDEBUG
+ error =
+ tp_emit(XPD_TPDU_type, p, p->tp_Xuna, 1, m);
+ p->tp_retrans = p->tp_Nretrans;
+
+ tp_ctimeout(p, TM_retrans, (int)p->tp_rxtcur);
+ SEQ_INC(p, p->tp_Xsndnxt);
+ }
+ if(trick_hc)
+ return error;
+ }
+ break;
+case 0x30:
+ {
+ struct sockbuf *sb = &p->tp_sock->so_snd;
+
+ IFDEBUG(D_ACKRECV)
+ printf("GOOD ACK seq 0x%x cdt 0x%x\n", e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_cdt);
+ ENDDEBUG
+ if( p->tp_class != TP_CLASS_0) {
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ sbwakeup(sb);
+ IFDEBUG(D_ACKRECV)
+ printf("GOOD ACK new sndnxt 0x%x\n", p->tp_sndnxt);
+ ENDDEBUG
+ }
+ break;
+case 0x31:
+ {
+ IFTRACE(D_ACKRECV)
+ tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
+ e->ev_union.EV_AK_TPDU.e_fcc_present, p->tp_r_subseq, e->ev_union.EV_AK_TPDU.e_subseq, 0);
+ ENDTRACE
+ if( p->tp_class != TP_CLASS_0 ) {
+
+ if ( !e->ev_union.EV_AK_TPDU.e_fcc_present ) {
+ /* send ACK with FCC */
+ IncStat( ts_ackreason[_ACK_FCC_] );
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 1, MNULL);
+ }
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ }
+ break;
+case 0x32:
+ {
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ tp_cuntimeout(p, TM_retrans);
+
+ sbwakeup( &p->tp_sock->so_snd );
+
+ /* resume normal data */
+ tp_send(p);
+ }
+ break;
+case 0x33:
+ {
+ IFTRACE(D_ACKRECV)
+ tptrace(TPPTmisc, "BOGUS XACK eventtype ", e->ev_number, 0, 0,0);
+ ENDTRACE
+ if( p->tp_class != TP_CLASS_0 ) {
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ }
+ break;
+case 0x34:
+ {
+ int timo;
+ IFTRACE(D_TIMER)
+ tptrace(TPPTsendack, -1, p->tp_lcredit, p->tp_sent_uwe,
+ p->tp_sent_lcdt, 0);
+ ENDTRACE
+ IncPStat(p, tps_n_TMsendack);
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL);
+ if (p->tp_fcredit == 0) {
+ if (p->tp_rxtshift < TP_MAXRXTSHIFT)
+ p->tp_rxtshift++;
+ timo = (p->tp_dt_ticks) << p->tp_rxtshift;
+ } else
+ timo = p->tp_sendack_ticks;
+ tp_ctimeout(p, TM_sendack, timo);
+ }
+ break;
+case 0x35:
+ {
+ if (sbspace(&p->tp_sock->so_rcv) > 0)
+ tp0_openflow(p);
+ }
+ break;
+case 0x36:
+ {
+ if( trick_hc ) {
+ SeqNum ack_thresh;
+ /*
+ * If the upper window edge has advanced a reasonable
+ * amount beyond what was known, send an ACK.
+ * A reasonable amount is 2 packets, unless the max window
+ * is only 1 or 2 packets, in which case we
+ * should send an ack for any advance in the upper window edge.
+ */
+ LOCAL_CREDIT(p);
+ ack_thresh = SEQ_SUB(p, p->tp_lcredit + p->tp_rcvnxt,
+ (p->tp_maxlcredit > 2 ? 2 : 1));
+ if (SEQ_GT(p, ack_thresh, p->tp_sent_uwe)) {
+ IncStat(ts_ackreason[_ACK_USRRCV_]);
+ p->tp_flags &= ~TPF_DELACK;
+ return tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL);
+ }
+ }
+ }
+ break;
+case 0x37:
+ {
+ if(trick_hc)
+ return ECONNABORTED;
+ }
+ break;
+case 0x38:
+ {
+ ASSERT( p->tp_state != TP_LISTENING );
+ tp_indicate(T_DISCONNECT, p, ECONNRESET);
+ tp_soisdisconnected(p);
+ }
+ break;
+ }
+return 0;
+}
+
+_XEBEC_PG int
+_Xebec_index( e,p )
+ struct tp_event *e;
+ tp_PCB_ *p;
+{
+switch( (e->ev_number<<4)+(p->tp_state) ) {
+case 0x12:
+ if ( p->tp_retrans > 0 ) return 0x1e;
+ else return 0x1f;
+case 0x13:
+ if ( p->tp_retrans > 0 ) return 0x2f;
+ else return 0x30;
+case 0x14:
+ if ( p->tp_retrans > 0 ) return 0x32;
+ else return 0x31;
+case 0x15:
+ if ( p->tp_retrans > 0 ) return 0x34;
+ else return 0x35;
+case 0x54:
+ if (p->tp_rxtshift < TP_NRETRANS) return 0x33;
+ else return 0x31;
+case 0x64:
+ if (p->tp_class == TP_CLASS_0) return 0x1a;
+ else return 0x1b;
+case 0x77:
+ if ( p->tp_class == TP_CLASS_0) return 0xd;
+ else return 0xe;
+case 0x86:
+ if ( e->ev_union.EV_DR_TPDU.e_sref != 0 ) return 0x2;
+ else return 0x3;
+case 0xa2:
+ if (p->tp_class == TP_CLASS_0) return 0x1c;
+ else return 0x1d;
+case 0xb2:
+ if (p->tp_class == TP_CLASS_0) return 0x5;
+ else return 0x0;
+case 0xb4:
+ if ( tp_goodack(p, e->ev_union.EV_AK_TPDU.e_cdt, e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_subseq) ) return 0x3a;
+ else return 0x3b;
+case 0xc3:
+ if ( IN_RWINDOW( p, e->ev_union.EV_DT_TPDU.e_seq,
+ p->tp_rcvnxt, SEQ(p, p->tp_rcvnxt + p->tp_lcredit)) ) return 0x21;
+ else return 0x24;
+case 0xc4:
+ if ( p->tp_class == TP_CLASS_0 ) return 0x22;
+ else if ( IN_RWINDOW( p, e->ev_union.EV_DT_TPDU.e_seq,
+ p->tp_rcvnxt, SEQ(p, p->tp_rcvnxt + p->tp_lcredit)) ) return 0x23;
+ else return 0x25;
+case 0xd3:
+ if (p->tp_Xrcvnxt == e->ev_union.EV_XPD_TPDU.e_seq) return 0x27;
+ else return 0x2a;
+case 0xd4:
+ if (p->tp_Xrcvnxt == e->ev_union.EV_XPD_TPDU.e_seq) return 0x27;
+ else return 0x29;
+case 0xe4:
+ if ( tp_goodXack(p, e->ev_union.EV_XAK_TPDU.e_seq) ) return 0x3c;
+ else return 0x3d;
+case 0x102:
+ if ( p->tp_class == TP_CLASS_0 ) return 0x2d;
+ else return 0x2e;
+case 0x104:
+ if ( p->tp_class == TP_CLASS_0 ) return 0x2d;
+ else return 0x2e;
+case 0x144:
+ if (p->tp_class == TP_CLASS_0) return 0x3f;
+ else return 0x40;
+case 0x162:
+ if (p->tp_class == TP_CLASS_0) return 0x2b;
+ else return 0x2c;
+case 0x172:
+ if ( p->tp_class != TP_CLASS_4 ) return 0x42;
+ else return 0x46;
+case 0x174:
+ if ( p->tp_class != TP_CLASS_4 ) return 0x42;
+ else return 0x47;
+case 0x177:
+ if ( p->tp_class != TP_CLASS_4 ) return 0x42;
+ else return 0x43;
+case 0x188:
+ if ( p->tp_class == TP_CLASS_0 ) return 0xf;
+ else if (tp_emit(CC_TPDU_type, p, 0,0, MCPY(p->tp_ucddata, M_NOWAIT)) == 0) return 0x10;
+ else return 0x11;
+default: return 0;
+} /* end switch */
+} /* _Xebec_index() */
+static int inx[26][9] = { {0,0,0,0,0,0,0,0,0,},
+ {0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,-1,-1,-1,-1,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,-1,0x0,0x0,0x0,0x0, },
+ {0x0,0x7,0x15,0x1b,-1,0x17,0x3,0xa,0x0, },
+ {0x0,0x19,0x6,0x20,0x37,0x8,0x3,-1,0x0, },
+ {0x0,0x14,0x13,0x13,0x13,0x16,-1,0xa,0x0, },
+ {0x0,0x7,0x6,0x1,0x9,0x18,0x3,0xa,0x0, },
+ {0x0,0x19,-1,0x1,0x37,0x8,0x3,0xa,0x0, },
+ {0x0,0x7,-1,0x26,-1,0x8,0x3,0xa,0x0, },
+ {0x0,0x7,0x6,-1,-1,0x8,0x3,0xa,0x0, },
+ {0x0,0x7,0x6,-1,-1,0x8,0x3,0xa,0x0, },
+ {0x0,0x7,0x6,0x1,-1,0x8,0x3,0xa,0x0, },
+ {0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,-1,0x2e,-1,0x0,0x4,0x0,0x2e, },
+ {0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,-1,0x0,0x41,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x28,0x0,0x41,0x0,0x0, },
+ {0x0,0xc,-1,0x2c,0x0,0x2c,0x4,0xc,0x2c, },
+ {0x0,0x49,-1,0x45,-1,0x44,0x48,-1,0x0, },
+ {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,-1, },
+};
+tp_driver(p, e)
+register tp_PCB_ *p;
+register struct tp_event *e;
+{
+ register int index, error=0;
+ struct act_ent *a;
+ static struct act_ent erroraction = {0,-1};
+
+ index = inx[1 + e->ev_number][p->tp_state];
+ if(index<0) index=_Xebec_index(e, p);
+ if (index==0) {
+ a = &erroraction;
+ } else
+ a = &statetable[index];
+
+ if(a->a_action)
+ error = _Xebec_action( a->a_action, e, p );
+ IFTRACE(D_DRIVER)
+ tptrace(DRIVERTRACE, a->a_newstate, p->tp_state, e->ev_number, a->a_action, 0);
+ ENDTRACE
+ if(error==0)
+ p->tp_state = a->a_newstate;
+ return error;
+}