diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /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.c | 997 |
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; +} |