xref: /original-bsd/sys/netiso/tp.trans (revision ba762ddc)
1/***********************************************************
2		Copyright IBM Corporation 1987
3
4                      All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that the name of IBM not be
11used in advertising or publicity pertaining to distribution of the
12software without specific, written prior permission.
13
14IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22******************************************************************/
23
24/*
25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
26 */
27/* $Header: tp.trans,v 5.1 88/10/12 12:22:07 root Exp $
28 *
29 * Transition file for TP.
30 *
31 * DO NOT:
32 * - change the order of any of the events or states.  to do so will
33 *   make tppt, netstat, etc. cease working.
34 *
35 * NOTE:
36 * some hooks exist for data on (dis)connect, but it's ***NOT***SUPPORTED***
37 * (read: may not work!)
38 *
39 * I tried to put everything that causes a change of state in here, hence
40 * there are some seemingly trivial events  like T_DETACH and T_LISTEN_req.
41 *
42 * Almost everything having to do w/ setting & cancelling timers is here
43 * but once it was debugged, I moved the setting of the
44 * keepalive (sendack) timer to tp_emit(), where an AK_TPDU is sent.
45 * This is so the code wouldn't be duplicated all over creation in here.
46 *
47 */
48*PROTOCOL tp
49
50*INCLUDE
51
52{
53/*	@(#)tp.trans	7.7 (Berkeley) 04/26/91 */
54#include "param.h"
55#include "socket.h"
56#include "socketvar.h"
57#include "protosw.h"
58#include "mbuf.h"
59#include "time.h"
60#include "errno.h"
61#include "../netiso/tp_param.h"
62#include "../netiso/tp_stat.h"
63#include "../netiso/tp_pcb.h"
64#include "../netiso/tp_tpdu.h"
65#include "../netiso/argo_debug.h"
66#include "../netiso/tp_trace.h"
67#include "../netiso/iso_errno.h"
68#include "../netiso/tp_seq.h"
69#include "../netiso/cons.h"
70
71#define DRIVERTRACE TPPTdriver
72#define sbwakeup(sb)	sowakeup(p->tp_sock, sb);
73#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0)
74
75static 	trick_hc = 1;
76
77int 	tp_emit(),
78		tp_goodack(),				tp_goodXack(),
79		tp_stash()
80;
81void	tp_indicate(),				tp_getoptions(),
82		tp_soisdisconnecting(), 	tp_soisdisconnected(),
83		tp_recycle_tsuffix(),
84		tp_etimeout(),				tp_euntimeout(),
85		tp_euntimeout_lss(),		tp_ctimeout(),
86		tp_cuntimeout(),			tp_ctimeout_MIN(),
87		tp_freeref(),				tp_detach(),
88		tp0_stash(), 				tp0_send(),
89		tp_netcmd(),				tp_send()
90;
91
92typedef  struct tp_pcb tpcb_struct;
93
94
95}
96
97*PCB    tpcb_struct 	SYNONYM  P
98
99*STATES
100
101TP_CLOSED
102TP_CRSENT
103TP_AKWAIT
104TP_OPEN
105TP_CLOSING
106TP_REFWAIT
107TP_LISTENING	/* Local to this implementation */
108TP_CONFIRMING	/* Local to this implementation */
109
110*EVENTS		{ struct timeval e_time; } 		SYNONYM  E
111
112 /*
113  * C (typically cancelled) timers  -
114  *
115  * let these be the first ones so for the sake of convenience
116  * their values are 0--> n-1
117  * DO NOT CHANGE THE ORDER OF THESE TIMER EVENTS!!
118  */
119 TM_inact
120 TM_retrans
121				/* TM_retrans is used for all
122				 * simple retransmissions - CR,CC,XPD,DR
123				 */
124
125 TM_sendack
126				/* TM_sendack does dual duty - keepalive AND sendack.
127				 * It's set w/ keepalive-ticks every time an ack is sent.
128				 * (this is done in (void) tp_emit() ).
129				 * It's cancelled and reset whenever a DT
130				 * arrives and it doesn't require immediate acking.
131				 * Note that in this case it's set w/ the minimum of
132				 * its prev value and the sendack-ticks value so the
133				 * purpose of the keepalive is preserved.
134				 */
135 TM_notused
136
137 /*
138  * E (typically expired) timers - these may be in any order.
139  * These cause procedures to be executed directly; may not
140  * cause an 'event' as we know them here.
141  */
142 TM_reference		{ SeqNum e_low; SeqNum e_high; int e_retrans; }
143 TM_data_retrans	{ SeqNum e_low; SeqNum e_high; int e_retrans; }
144
145/* NOTE: in tp_input is a minor optimization that assumes that
146 * for all tpdu types that can take e_data and e_datalen, these
147 * fields fall in the same place in the event structure, that is,
148 * e_data is the first field and e_datalen is the 2nd field.
149 */
150
151 ER_TPDU  	 	{
152				  u_char		e_reason;
153				}
154 CR_TPDU  	 	{ struct mbuf 	*e_data;	/* first field */
155				  int 			e_datalen; /* 2nd field */
156				  u_int			e_cdt;
157				}
158 DR_TPDU   	 	{ struct mbuf 	*e_data;	/* first field */
159				  int 			e_datalen; /* 2nd field */
160				  u_short		e_sref;
161				  u_char		e_reason;
162				}
163 DC_TPDU
164 CC_TPDU   	 	{ struct mbuf 	*e_data;	/* first field */
165				  int 			e_datalen; /* 2nd field */
166				  u_short		e_sref;
167				  u_int			e_cdt;
168				}
169 AK_TPDU		{ u_int			e_cdt;
170				  SeqNum 	 	e_seq;
171				  SeqNum 	 	e_subseq;
172				  u_char 	 	e_fcc_present;
173				}
174 DT_TPDU		{ struct mbuf	*e_data; 	/* first field */
175				  int 			e_datalen; /* 2nd field */
176				  u_int 		e_eot;
177				  SeqNum		e_seq;
178				}
179 XPD_TPDU		{ struct mbuf 	*e_data;	/* first field */
180				  int 			e_datalen; 	/* 2nd field */
181				  SeqNum 		e_seq;
182				}
183 XAK_TPDU		{ SeqNum 		e_seq;		}
184
185 T_CONN_req
186 T_DISC_req		{ u_char		e_reason; 	}
187 T_LISTEN_req
188 T_DATA_req
189 T_XPD_req
190 T_USR_rcvd
191 T_USR_Xrcvd
192 T_DETACH
193 T_NETRESET
194 T_ACPT_req
195
196
197*TRANSITIONS
198
199
200/* TP_AKWAIT doesn't exist in TP 0 */
201SAME			<==			TP_AKWAIT			[ CC_TPDU, DC_TPDU, XAK_TPDU ]
202	DEFAULT
203	NULLACTION
204;
205
206
207/* applicable in TP4, TP0 */
208SAME			<==			TP_REFWAIT								DR_TPDU
209	( $$.e_sref !=  0 )
210	{
211		(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
212	}
213;
214
215/* applicable in TP4, TP0 */
216SAME			<==			TP_REFWAIT			[ CR_TPDU, CC_TPDU, DT_TPDU,
217					DR_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU, DC_TPDU, ER_TPDU ]
218	DEFAULT
219	{
220#		ifdef TP_DEBUG
221		if( $E.ev_number != AK_TPDU )
222			printf("TPDU 0x%x in REFWAIT!!!!\n", $E.ev_number);
223#		endif TP_DEBUG
224	}
225;
226
227/* applicable in TP4, TP0 */
228SAME			<==			TP_REFWAIT				[ T_DETACH, T_DISC_req ]
229	DEFAULT
230	NULLACTION
231;
232
233/* applicable in TP4, TP0 */
234SAME			<==			TP_CRSENT								 AK_TPDU
235	($P.tp_class == TP_CLASS_0)
236	{
237		/* oh, man is this grotesque or what? */
238		(void) tp_goodack($P, $$.e_cdt, $$.e_seq,  $$.e_subseq);
239		/* but it's necessary because this pseudo-ack may happen
240		 * before the CC arrives, but we HAVE to adjust the
241		 * snduna as a result of the ack, WHENEVER it arrives
242		 */
243	}
244;
245
246/* applicable in TP4, TP0 */
247SAME			<==			TP_CRSENT
248					[ CR_TPDU, DC_TPDU, DT_TPDU, XPD_TPDU,  XAK_TPDU ]
249	DEFAULT
250	NULLACTION
251;
252
253/* applicable in TP4, TP0 */
254SAME			<==			TP_CLOSED					[ DT_TPDU, XPD_TPDU,
255										ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
256	DEFAULT
257	NULLACTION
258;
259
260/* TP_CLOSING doesn't exist in TP 0 */
261SAME 			<== 		TP_CLOSING
262					[ CC_TPDU, CR_TPDU, DT_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU ]
263	DEFAULT
264	NULLACTION
265;
266
267
268/* DC_TPDU doesn't exist in TP 0 */
269SAME			<==			TP_OPEN						  DC_TPDU
270	DEFAULT
271	NULLACTION
272;
273
274/* applicable in TP4, TP0 */
275SAME			<==		 	TP_LISTENING  [DR_TPDU, CC_TPDU, DT_TPDU, XPD_TPDU,
276										 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
277	DEFAULT
278	NULLACTION
279;
280
281/* applicable in TP4, TP0 */
282TP_LISTENING	<==			TP_CLOSED  							T_LISTEN_req
283	DEFAULT
284	NULLACTION
285;
286
287/* applicable in TP4, TP0 */
288TP_CLOSED  		<== 		[ TP_LISTENING, TP_CLOSED ] 			T_DETACH
289	DEFAULT
290	{
291		tp_detach($P);
292	}
293;
294
295TP_CONFIRMING	<==		 TP_LISTENING  								CR_TPDU
296	( $P.tp_class == TP_CLASS_0)
297	{
298		$P.tp_refp->tpr_state = REF_OPEN; /* has timers ??? */
299	}
300;
301
302TP_CONFIRMING		<==		 TP_LISTENING  							CR_TPDU
303	DEFAULT
304	{
305		IFTRACE(D_CONN)
306			tptrace(TPPTmisc, "CR datalen data", $$.e_datalen, $$.e_data,0,0);
307		ENDTRACE
308		IFDEBUG(D_CONN)
309			printf("CR datalen 0x%x data 0x%x", $$.e_datalen, $$.e_data);
310		ENDDEBUG
311		$P.tp_refp->tpr_state = REF_OPEN; /* has timers */
312		$P.tp_fcredit = $$.e_cdt;
313
314		if ($$.e_datalen > 0) {
315			/* n/a for class 0 */
316			ASSERT($P.tp_Xrcv.sb_cc == 0);
317			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
318			$$.e_data = MNULL;
319		}
320	}
321;
322
323TP_OPEN		<==		 TP_CONFIRMING  								T_ACPT_req
324	( $P.tp_class == TP_CLASS_0 )
325	{
326		IncStat(ts_tp0_conn);
327		IFTRACE(D_CONN)
328			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
329		ENDTRACE
330		IFDEBUG(D_CONN)
331			printf("Confirming connection: $P" );
332		ENDDEBUG
333		soisconnected($P.tp_sock);
334		(void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ;
335		$P.tp_fcredit = 1;
336	}
337;
338
339TP_AKWAIT		<==		 TP_CONFIRMING  							T_ACPT_req
340	(tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0)
341	{
342		IncStat(ts_tp4_conn); /* even though not quite open */
343		IFTRACE(D_CONN)
344			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
345		ENDTRACE
346		IFDEBUG(D_CONN)
347			printf("Confirming connection: $P" );
348		ENDDEBUG
349		soisconnecting($P.tp_sock);
350		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($P.tp_fcredit > 0))
351			$P.tp_cong_win = $P.tp_fcredit;
352		$P.tp_retrans = $P.tp_Nretrans;
353		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
354	}
355;
356
357/* TP4 only */
358TP_CLOSED		<==		 TP_CONFIRMING								T_ACPT_req
359	DEFAULT /* emit failed */
360	{
361		register struct tp_ref *r = $P.tp_refp;
362
363		IFDEBUG(D_CONN)
364			printf("event: CR_TPDU emit CC failed done " );
365		ENDDEBUG
366		soisdisconnected($P.tp_sock);
367		tp_recycle_tsuffix( $P );
368		tp_freeref(r);
369		tp_detach($P);
370	}
371;
372
373/* applicable in TP4, TP0 */
374TP_CRSENT		<==		TP_CLOSED								T_CONN_req
375	DEFAULT
376	{
377		int error;
378		struct mbuf *data = MNULL;
379
380		IFTRACE(D_CONN)
381			tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
382			$P.tp_ucddata, 0, 0);
383		ENDTRACE
384		data =  MCPY($P.tp_ucddata, M_WAIT);
385		if (data) {
386			IFDEBUG(D_CONN)
387				printf("T_CONN_req.trans m_copy cc 0x%x\n",
388					$P.tp_ucddata);
389				dump_mbuf(data, "sosnd @ T_CONN_req");
390			ENDDEBUG
391		}
392
393		if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
394			return error; /* driver WON'T change state; will return error */
395
396		$P.tp_refp->tpr_state = REF_OPEN; /* has timers */
397		if($P.tp_class != TP_CLASS_0) {
398			$P.tp_retrans = $P.tp_Nretrans;
399			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
400		}
401	}
402;
403
404/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
405TP_REFWAIT 		<==		[ TP_CRSENT, TP_AKWAIT, TP_OPEN ] 			DR_TPDU
406	DEFAULT
407	{
408		sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */
409		if ($$.e_datalen > 0) {
410			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
411			$$.e_data = MNULL;
412		}
413		tp_indicate(T_DISCONNECT, $P, 0);
414		tp_soisdisconnected($P);
415		if ($P.tp_class != TP_CLASS_0) {
416			if ($P.tp_state == TP_OPEN ) {
417				tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
418				tp_cuntimeout($P.tp_refp, TM_retrans);
419				tp_cuntimeout($P.tp_refp, TM_inact);
420				tp_cuntimeout($P.tp_refp, TM_sendack);
421			}
422			tp_cuntimeout($P.tp_refp, TM_retrans);
423			if( $$.e_sref !=  0 )
424				(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
425		}
426	}
427;
428
429SAME 			<==		TP_CLOSED 									DR_TPDU
430	DEFAULT
431	{
432		if( $$.e_sref != 0 )
433			(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
434		/* reference timer already set - reset it to be safe (???) */
435		tp_euntimeout($P.tp_refp, TM_reference); /* all */
436		tp_etimeout($P.tp_refp, TM_reference, 0, 0, 0, (int)$P.tp_refer_ticks);
437	}
438;
439
440/* NBS(34) */
441TP_REFWAIT 		<==  	TP_CRSENT  									ER_TPDU
442	DEFAULT
443	{
444		tp_cuntimeout($P.tp_refp, TM_retrans);
445		tp_indicate(ER_TPDU, $P, $$.e_reason);
446		tp_soisdisconnected($P);
447	}
448;
449
450/* NBS(27) */
451TP_REFWAIT		<==		TP_CLOSING									DR_TPDU
452	DEFAULT
453	{
454		tp_cuntimeout($P.tp_refp, TM_retrans);
455		tp_soisdisconnected($P);
456	}
457;
458/* these two transitions are the same but can't be combined because xebec
459 * can't handle the use of $$.e_reason if they're combined
460 */
461/* NBS(27) */
462TP_REFWAIT		<==		TP_CLOSING									ER_TPDU
463	DEFAULT
464	{
465		tp_indicate(ER_TPDU, $P, $$.e_reason);
466		tp_cuntimeout($P.tp_refp, TM_retrans);
467		tp_soisdisconnected($P);
468	}
469;
470/* NBS(27) */
471TP_REFWAIT		<==		TP_CLOSING									DC_TPDU
472	DEFAULT
473	{
474		tp_cuntimeout($P.tp_refp, TM_retrans);
475		tp_soisdisconnected($P);
476	}
477;
478
479/* NBS(21) */
480SAME 			<== 	TP_CLOSED 						[ CC_TPDU, CR_TPDU ]
481	DEFAULT
482	{	/* don't ask me why we have to do this - spec says so */
483		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
484		/* don't bother with retransmissions of the DR */
485	}
486;
487
488/* NBS(34) */
489TP_REFWAIT 		<== 	TP_OPEN  				 					ER_TPDU
490	($P.tp_class == TP_CLASS_0)
491	{
492		tp_soisdisconnecting($P.tp_sock);
493		tp_indicate(ER_TPDU, $P, $$.e_reason);
494		tp_soisdisconnected($P);
495		tp_netcmd( $P, CONN_CLOSE );
496	}
497;
498
499TP_CLOSING 		<== 	[ TP_AKWAIT, TP_OPEN ]  					ER_TPDU
500	DEFAULT
501	{
502		if ($P.tp_state == TP_OPEN) {
503			tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
504			tp_cuntimeout($P.tp_refp, TM_inact);
505			tp_cuntimeout($P.tp_refp, TM_sendack);
506		}
507		tp_soisdisconnecting($P.tp_sock);
508		tp_indicate(ER_TPDU, $P, $$.e_reason);
509		$P.tp_retrans = $P.tp_Nretrans;
510		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
511		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
512	}
513;
514/* NBS(6) */
515TP_OPEN			<==		TP_CRSENT									CC_TPDU
516	($P.tp_class == TP_CLASS_0)
517	{
518		tp_cuntimeout($P.tp_refp, TM_retrans);
519		IncStat(ts_tp0_conn);
520		$P.tp_fcredit = 1;
521		soisconnected($P.tp_sock);
522	}
523;
524
525TP_OPEN			<==		TP_CRSENT									CC_TPDU
526	DEFAULT
527	{
528		IFDEBUG(D_CONN)
529			printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
530				(int)$P.tp_flags);
531		ENDDEBUG
532		IncStat(ts_tp4_conn);
533		$P.tp_fref = $$.e_sref;
534		$P.tp_fcredit = $$.e_cdt;
535		$P.tp_ackrcvd = 0;
536		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0))
537			$P.tp_cong_win = $$.e_cdt;
538		tp_getoptions($P);
539		tp_cuntimeout($P.tp_refp, TM_retrans);
540		if ($P.tp_ucddata) {
541			IFDEBUG(D_CONN)
542				printf("dropping user connect data cc 0x%x\n",
543					$P.tp_ucddata->m_len);
544			ENDDEBUG
545			m_freem($P.tp_ucddata);
546			$P.tp_ucddata = 0;
547		}
548		soisconnected($P.tp_sock);
549		if ($$.e_datalen > 0) {
550			ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
551			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
552			$$.e_data = MNULL;
553		}
554
555		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
556		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
557	}
558;
559
560/* TP4 only */
561SAME			<==		TP_CRSENT									TM_retrans
562	(	$P.tp_retrans > 0 )
563	{
564		struct mbuf *data = MNULL;
565		int error;
566
567		IncStat(ts_retrans_cr);
568		$P.tp_cong_win = 1;
569		$P.tp_ackrcvd = 0;
570		data = MCPY($P.tp_ucddata, M_NOWAIT);
571		if($P.tp_ucddata) {
572			IFDEBUG(D_CONN)
573				printf("TM_retrans.trans m_copy cc 0x%x\n", data);
574				dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
575			ENDDEBUG
576			if( data == MNULL )
577				return ENOBUFS;
578		}
579
580		$P.tp_retrans --;
581		if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
582			$P.tp_sock->so_error = error;
583		}
584		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
585	}
586;
587
588/* TP4 only  */
589TP_REFWAIT		<==		TP_CRSENT									TM_retrans
590	DEFAULT /* no more CR retransmissions */
591	{
592		IncStat(ts_conn_gaveup);
593		$P.tp_sock->so_error = ETIMEDOUT;
594		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
595		tp_soisdisconnected($P);
596	}
597;
598
599/* TP4 only */
600SAME 			<==	 TP_AKWAIT											CR_TPDU
601	DEFAULT
602	/* duplicate CR (which doesn't really exist in the context of
603	 * a connectionless network layer)
604	 * Doesn't occur in class 0.
605	 */
606	{
607		int error;
608		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
609
610		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
611			$P.tp_sock->so_error = error;
612		}
613		$P.tp_retrans = $P.tp_Nretrans;
614		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
615	}
616;
617
618/* TP4 only */
619TP_OPEN			<==		TP_AKWAIT 										DT_TPDU
620	( IN_RWINDOW( $P, $$.e_seq,
621					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
622	{
623		int doack;
624
625		/*
626		 * Get rid of any confirm or connect data, so that if we
627		 * crash or close, it isn't thought of as disconnect data.
628		 */
629		if ($P.tp_ucddata) {
630			m_freem($P.tp_ucddata);
631			$P.tp_ucddata = 0;
632		}
633		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
634		tp_cuntimeout($P.tp_refp, TM_retrans);
635		soisconnected($P.tp_sock);
636		tp_getoptions($P);
637		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
638
639		/* see also next 2 transitions, if you make any changes */
640
641		doack = tp_stash($P, $E);
642		IFDEBUG(D_DATA)
643			printf("tp_stash returns %d\n",doack);
644		ENDDEBUG
645
646		if(doack) {
647			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
648			tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
649		} else
650			tp_ctimeout( $P.tp_refp, TM_sendack, (int)$P.tp_sendack_ticks);
651
652		IFDEBUG(D_DATA)
653			printf("after stash calling sbwakeup\n");
654		ENDDEBUG
655	}
656;
657
658SAME			<==		TP_OPEN 									DT_TPDU
659	( $P.tp_class == TP_CLASS_0 )
660	{
661		tp0_stash($P, $E);
662		sbwakeup( &$P.tp_sock->so_rcv );
663
664		IFDEBUG(D_DATA)
665			printf("after stash calling sbwakeup\n");
666		ENDDEBUG
667	}
668;
669
670/* TP4 only */
671SAME			<==		TP_OPEN 									DT_TPDU
672	( IN_RWINDOW( $P, $$.e_seq,
673					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
674	{
675		int doack; /* tells if we must ack immediately */
676
677		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
678		sbwakeup( &$P.tp_sock->so_rcv );
679
680		doack = tp_stash($P, $E);
681		IFDEBUG(D_DATA)
682			printf("tp_stash returns %d\n",doack);
683		ENDDEBUG
684
685		if(doack)
686			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
687		else
688			tp_ctimeout_MIN( $P.tp_refp, TM_sendack, (int)$P.tp_sendack_ticks);
689
690		IFDEBUG(D_DATA)
691			printf("after stash calling sbwakeup\n");
692		ENDDEBUG
693	}
694;
695
696/* Not in window  - we must ack under certain circumstances, namely
697 * a) if the seq number is below lwe but > lwe - (max credit ever given)
698 * (to handle lost acks) Can use max-possible-credit for this ^^^.
699 * and
700 * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
701 *
702 * (see 12.2.3.8.1 of ISO spec, p. 73)
703 * We just always ack.
704 */
705/* TP4 only */
706SAME 			<== 	[ TP_OPEN, TP_AKWAIT ]							DT_TPDU
707	DEFAULT /* Not in window */
708	{
709		IFTRACE(D_DATA)
710			tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
711				$$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
712		ENDTRACE
713		IncStat(ts_dt_niw);
714		m_freem($$.e_data);
715		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
716		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
717	}
718;
719
720/* TP4 only */
721TP_OPEN			<==		TP_AKWAIT										AK_TPDU
722	DEFAULT
723	{
724		if ($P.tp_ucddata) {
725			m_freem($P.tp_ucddata);
726			$P.tp_ucddata = 0;
727		}
728		(void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
729		tp_cuntimeout($P.tp_refp, TM_retrans);
730
731		tp_getoptions($P);
732		soisconnected($P.tp_sock);
733		IFTRACE(D_CONN)
734			struct socket *so = $P.tp_sock;
735			tptrace(TPPTmisc,
736			"called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
737				so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
738			tptrace(TPPTmisc,
739			"called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
740				so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
741		ENDTRACE
742
743		tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
744		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
745	}
746;
747
748/* TP4 only */
749TP_OPEN 		<== 	[ TP_OPEN, TP_AKWAIT ]						XPD_TPDU
750	($P.tp_Xrcvnxt == $$.e_seq)
751	{
752		if( $P.tp_state == TP_AKWAIT ) {
753			if ($P.tp_ucddata) {
754				m_freem($P.tp_ucddata);
755				$P.tp_ucddata = 0;
756			}
757			tp_cuntimeout($P.tp_refp, TM_retrans);
758			tp_getoptions($P);
759			soisconnected($P.tp_sock);
760			tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
761			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
762		}
763		IFTRACE(D_XPD)
764		tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
765				$P.tp_Xrcvnxt,$$.e_seq,  $$.e_datalen, $$.e_data->m_len);
766		ENDTRACE
767
768		$P.tp_sock->so_state |= SS_RCVATMARK;
769		$$.e_data->m_flags |= M_EOR;
770		sbinsertoob(&$P.tp_Xrcv, $$.e_data);
771		IFDEBUG(D_XPD)
772			dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
773		ENDDEBUG
774		tp_indicate(T_XDATA, $P, 0);
775		sbwakeup( &$P.tp_Xrcv );
776
777		(void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
778		SEQ_INC($P, $P.tp_Xrcvnxt);
779	}
780;
781
782/* TP4 only */
783SAME			<==		TP_OPEN 									T_USR_Xrcvd
784	DEFAULT
785	{
786		if( $P.tp_Xrcv.sb_cc == 0 ) {
787			/* kludge for select(): */
788			/* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
789		}
790	}
791	/* OLD WAY:
792	 * Ack only after the user receives the XPD.  This is better for
793	 * users that use one XPD right after another.
794	 * Acking right away (the NEW WAY, see the prev. transition) is
795	 * better for occasional * XPD, when the receiving user doesn't
796	 * want to read the XPD immediately (which is session's behavior).
797	 *
798		int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
799		SEQ_INC($P, $P.tp_Xrcvnxt);
800		return error;
801	*/
802;
803
804/* NOTE: presently if the user doesn't read the connection data
805 * before and expedited data PDU comes in, the connection data will
806 * be dropped. This is a bug.  To avoid it, we need somewhere else
807 * to put the connection data.
808 * On the other hand, we need not to have it sitting around forever.
809 * This is a problem with the idea of trying to accommodate
810 * data on connect w/ a passive-open user interface.
811 */
812/* TP4 only */
813
814SAME	 		<== 	[ TP_AKWAIT, TP_OPEN ] 							XPD_TPDU
815	DEFAULT /* not in window or cdt==0 */
816	{
817		IFTRACE(D_XPD)
818			tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
819				$P.tp_Xrcvnxt, $$.e_seq,  $P.tp_Xrcv.sb_cc , 0);
820		ENDTRACE
821		if( $P.tp_Xrcvnxt != $$.e_seq )
822			IncStat(ts_xpd_niw);
823		if( $P.tp_Xrcv.sb_cc ) {
824			/* might as well kick 'em again */
825			tp_indicate(T_XDATA, $P, 0);
826			IncStat(ts_xpd_dup);
827		}
828		m_freem($$.e_data);
829		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
830		/* don't send an xack because the xak gives "last one received", not
831		 * "next one i expect" (dumb)
832		 */
833	}
834;
835
836/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
837 * to detach all its "children"
838 * Also (CRSENT) when user kills a job that's doing a connect()
839 */
840TP_REFWAIT		<== 	TP_CRSENT 										T_DETACH
841	($P.tp_class == TP_CLASS_0)
842	{
843		struct socket *so = $P.tp_sock;
844
845		/* detach from parent socket so it can finish closing */
846		if (so->so_head) {
847			if (!soqremque(so, 0) && !soqremque(so, 1))
848				panic("tp: T_DETACH");
849			so->so_head = 0;
850		}
851		tp_soisdisconnecting($P.tp_sock);
852		tp_netcmd( $P, CONN_CLOSE);
853		tp_soisdisconnected($P);
854	}
855;
856
857/* TP4 only */
858TP_CLOSING		<== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ]	T_DETACH
859	DEFAULT
860	{
861		struct socket *so = $P.tp_sock;
862		struct mbuf *data = MNULL;
863
864		/* detach from parent socket so it can finish closing */
865		if (so->so_head) {
866			if (!soqremque(so, 0) && !soqremque(so, 1))
867				panic("tp: T_DETACH");
868			so->so_head = 0;
869		}
870		if ($P.tp_state != TP_CLOSING) {
871			tp_soisdisconnecting($P.tp_sock);
872			data = MCPY($P.tp_ucddata, M_NOWAIT);
873			(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
874			$P.tp_retrans = $P.tp_Nretrans;
875			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
876		}
877	}
878;
879
880TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT ]		 	  			T_DISC_req
881	( $P.tp_class == TP_CLASS_0 )
882	{
883		tp_soisdisconnecting($P.tp_sock);
884		tp_netcmd( $P, CONN_CLOSE);
885		tp_soisdisconnected($P);
886	}
887;
888
889/* TP4 only */
890TP_CLOSING		<==	[ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ]  T_DISC_req
891	DEFAULT
892	{
893		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
894
895		if($P.tp_state == TP_OPEN) {
896			tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
897			tp_cuntimeout($P.tp_refp, TM_inact);
898			tp_cuntimeout($P.tp_refp, TM_sendack);
899		}
900		if (data) {
901			IFDEBUG(D_CONN)
902				printf("T_DISC_req.trans tp_ucddata 0x%x\n",
903					$P.tp_ucddata);
904				dump_mbuf(data, "ucddata @ T_DISC_req");
905			ENDDEBUG
906		}
907		tp_soisdisconnecting($P.tp_sock);
908		$P.tp_retrans = $P.tp_Nretrans;
909		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
910
911		if( trick_hc )
912			return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
913	}
914;
915
916/* TP4 only */
917SAME			<==		TP_AKWAIT									TM_retrans
918	( $P.tp_retrans > 0 )
919	{
920		int error;
921		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
922
923		IncStat(ts_retrans_cc);
924		$P.tp_retrans --;
925		$P.tp_cong_win = 1;
926		$P.tp_ackrcvd = 0;
927
928		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
929			$P.tp_sock->so_error = error;
930		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
931	}
932;
933
934/* TP4 only */
935TP_CLOSING		<==		TP_AKWAIT									TM_retrans
936	DEFAULT  /* out of time */
937	{
938		IncStat(ts_conn_gaveup);
939		tp_soisdisconnecting($P.tp_sock);
940		$P.tp_sock->so_error = ETIMEDOUT;
941		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
942		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
943		$P.tp_retrans = $P.tp_Nretrans;
944		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
945	}
946;
947
948/* the retrans timers had better go off BEFORE the inactivity timer does,
949 * if transmissions are going on.
950 * (i.e., TM_inact should be greater than timer for all retrans plus ack
951 * turnaround)
952 */
953/* TP4 only */
954TP_CLOSING 		<==		TP_OPEN		   [ TM_inact, TM_retrans, TM_data_retrans ]
955	DEFAULT
956	{
957		tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
958		tp_cuntimeout($P.tp_refp, TM_inact);
959		tp_cuntimeout($P.tp_refp, TM_sendack);
960
961		IncStat(ts_conn_gaveup);
962		tp_soisdisconnecting($P.tp_sock);
963		$P.tp_sock->so_error = ETIMEDOUT;
964		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
965		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
966		$P.tp_retrans = $P.tp_Nretrans;
967		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
968	}
969;
970
971/* TP4 only */
972SAME			<==		TP_OPEN										TM_retrans
973	( $P.tp_retrans > 0 )
974	{
975		$P.tp_cong_win = 1;
976		$P.tp_ackrcvd = 0;
977		/* resume XPD */
978		if	( $P.tp_Xsnd.sb_mb )  {
979			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
980			/* m_copy doesn't preserve the m_xlink field, but at this pt.
981			 * that doesn't matter
982			 */
983
984			IFTRACE(D_XPD)
985				tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndhiwat snduna",
986					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
987					$P.tp_snduna);
988			ENDTRACE
989			IFDEBUG(D_XPD)
990				dump_mbuf(m, "XPD retrans emitting M");
991			ENDDEBUG
992			IncStat(ts_retrans_xpd);
993			$P.tp_retrans --;
994			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
995			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
996		}
997	}
998;
999
1000/* TP4 only */
1001SAME 			<==		TP_OPEN									TM_data_retrans
1002	( $$.e_retrans > 0 )
1003	{
1004		register 	SeqNum			low, lowsave = 0;
1005		register	struct tp_rtc 	*r = $P.tp_snduna_rtc;
1006		register	struct mbuf 	*m;
1007		register	SeqNum			high = $$.e_high;
1008
1009		low = $P.tp_snduna;
1010		lowsave = high = low;
1011
1012		tp_euntimeout_lss($P.tp_refp, TM_data_retrans,
1013			SEQ_ADD($P, $P.tp_sndhiwat, 1));
1014		$P.tp_retrans_hiwat = $P.tp_sndhiwat;
1015
1016		if (($P.tp_rx_strat & TPRX_EACH) == 0)
1017			high = (high>low)?low:high;
1018
1019		if( $P.tp_rx_strat & TPRX_USE_CW ) {
1020			register int i;
1021
1022			$P.tp_cong_win = 1;
1023			$P.tp_ackrcvd = 0;
1024			i = SEQ_ADD($P, low, $P.tp_cong_win);
1025
1026			high = SEQ_MIN($P, high, $P.tp_sndhiwat);
1027
1028		}
1029
1030		while( SEQ_LEQ($P, low, high) ){
1031			if ( r == (struct tp_rtc *)0 ){
1032				IFDEBUG(D_RTC)
1033					printf( "tp: retrans rtc list is GONE!\n");
1034				ENDDEBUG
1035				break;
1036			}
1037			if ( r->tprt_seq == low ){
1038				if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))== MNULL)
1039					break;
1040				(void) tp_emit(DT_TPDU_type, $P, low, r->tprt_eot, m);
1041				IncStat(ts_retrans_dt);
1042				SEQ_INC($P, low );
1043			}
1044			r = r->tprt_next;
1045		}
1046/* CE_BIT
1047		if ( SEQ_LEQ($P, lowsave, high) ){
1048*/
1049			$$.e_retrans --;
1050			tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)lowsave,
1051					(caddr_t)high, $$.e_retrans,
1052					($P.tp_Nretrans - $$.e_retrans) * (int)$P.tp_dt_ticks);
1053/* CE_BIT
1054		}
1055*/
1056	}
1057;
1058
1059/* TP4 only */
1060SAME	 		<==		TP_CLOSING									TM_retrans
1061	(	$P.tp_retrans > 0 )
1062	{
1063		$P.tp_retrans --;
1064		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
1065		IncStat(ts_retrans_dr);
1066		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
1067	}
1068;
1069
1070/* TP4 only */
1071TP_REFWAIT 		<==		TP_CLOSING									TM_retrans
1072	DEFAULT	/* no more retrans - gave up */
1073	{
1074		$P.tp_sock->so_error = ETIMEDOUT;
1075		$P.tp_refp->tpr_state = REF_FROZEN;
1076		tp_recycle_tsuffix( $P );
1077		tp_etimeout($P.tp_refp, TM_reference, 0,0,0, (int)$P.tp_refer_ticks);
1078	}
1079;
1080
1081/*
1082 * The resources are kept around until the ref timer goes off.
1083 * The suffices are wiped out sooner so they can be reused right away.
1084 */
1085/* applicable in TP4, TP0 */
1086TP_CLOSED 		<==		TP_REFWAIT 									TM_reference
1087	DEFAULT
1088	{
1089		tp_freeref($P.tp_refp);
1090		tp_detach($P);
1091	}
1092;
1093
1094/* applicable in TP4, TP0 */
1095/* A duplicate CR from connectionless network layer can't happen */
1096SAME 			<== 	TP_OPEN 							[ CR_TPDU, CC_TPDU ]
1097	DEFAULT
1098	{
1099		if( $P.tp_class != TP_CLASS_0) {
1100			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1101			if ( $E.ev_number == CC_TPDU )
1102				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1103		}
1104		/* ignore it if class 0 - state tables are blank for this */
1105	}
1106;
1107
1108/* applicable in TP4, TP0 */
1109SAME			<== 	TP_OPEN									T_DATA_req
1110	DEFAULT
1111	{
1112		IFTRACE(D_DATA)
1113			tptrace(TPPTmisc, "T_DATA_req sndhiwat snduna fcredit, tpcb",
1114				$P.tp_sndhiwat, $P.tp_snduna, $P.tp_fcredit, $P);
1115		ENDTRACE
1116
1117		tp_send($P);
1118	}
1119;
1120
1121/* TP4 only */
1122SAME			<==		TP_OPEN										T_XPD_req
1123	DEFAULT
1124		/* T_XPD_req was issued by sosend iff xpd socket buf was empty
1125		 * at time of sosend(),
1126		 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
1127		 */
1128	{
1129		int error = 0;
1130
1131		/* resume XPD */
1132		if	( $P.tp_Xsnd.sb_mb )  {
1133			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
1134			/* m_copy doesn't preserve the m_xlink field, but at this pt.
1135			 * that doesn't matter
1136			 */
1137
1138			IFTRACE(D_XPD)
1139				tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndhiwat snduna",
1140					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
1141					$P.tp_snduna);
1142			ENDTRACE
1143			IFDEBUG(D_XPD)
1144				printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
1145				dump_mbuf(m, "XPD req emitting M");
1146			ENDDEBUG
1147			error =
1148				tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1149			$P.tp_retrans = $P.tp_Nretrans;
1150			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1151			SEQ_INC($P, $P.tp_Xsndnxt);
1152		}
1153		if(trick_hc)
1154			return error;
1155	}
1156;
1157
1158/* TP4, faked ack in TP0 when cons send completes */
1159SAME 			<==		TP_OPEN 									AK_TPDU
1160	( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq)  )
1161
1162	/* tp_goodack == true means
1163	 * EITHER it actually acked something heretofore unacknowledged
1164	 * OR no news but the credit should be processed.
1165	 */
1166	{
1167		IFDEBUG(D_ACKRECV)
1168			printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
1169		ENDDEBUG
1170		if( $P.tp_class != TP_CLASS_0) {
1171			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1172			tp_euntimeout_lss($P.tp_refp, TM_data_retrans, $$.e_seq);
1173		}
1174		sbwakeup( &$P.tp_sock->so_snd );
1175
1176		if ($P.tp_sndhiwat <= $P.tp_retrans_hiwat &&
1177			$P.tp_snduna <= $P.tp_retrans_hiwat) {
1178
1179			register    struct mbuf     *m;
1180			/* extern      struct mbuf     *m_copy(); */
1181			register    struct tp_rtc   *r;
1182			SeqNum      high, retrans, low_save;
1183
1184			high = SEQ_MIN($P, SEQ_ADD($P, $P.tp_snduna,
1185					MIN($P.tp_cong_win, $P.tp_fcredit)) - 1,
1186					$P.tp_sndhiwat);
1187			low_save = retrans = SEQ_MAX($P, SEQ_ADD($P, $P.tp_last_retrans, 1),
1188					$P.tp_snduna);
1189			for (; SEQ_LEQ($P, retrans, high); SEQ_INC($P, retrans)) {
1190
1191				for (r = $P.tp_snduna_rtc; r; r = r->tprt_next){
1192					if ( r->tprt_seq == retrans ){
1193						if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))
1194								== MNULL)
1195							break;
1196						(void) tp_emit(DT_TPDU_type, $P, retrans,
1197							r->tprt_eot, m);
1198						$P.tp_last_retrans = retrans;
1199						IncStat(ts_retrans_dt);
1200						break;
1201					}
1202				}
1203				if ( r == (struct tp_rtc *)0 ){
1204					IFDEBUG(D_RTC)
1205						printf( "tp: retrans rtc list is GONE!\n");
1206					ENDDEBUG
1207					break;
1208				}
1209			}
1210			tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)low_save,
1211					(caddr_t)high, $P.tp_retrans, (int)$P.tp_dt_ticks);
1212			if (SEQ_DEC($P, retrans) == $P.tp_retrans_hiwat)
1213				tp_send($P);
1214		}
1215		else {
1216			tp_send($P);
1217		}
1218		IFDEBUG(D_ACKRECV)
1219			printf("GOOD ACK new sndhiwat 0x%x\n", $P.tp_sndhiwat);
1220		ENDDEBUG
1221	}
1222;
1223
1224/* TP4, and TP0 after sending a CC or possibly a CR */
1225SAME			<==		TP_OPEN 			 						 AK_TPDU
1226	DEFAULT
1227	{
1228		IFTRACE(D_ACKRECV)
1229			tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
1230				$$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
1231		ENDTRACE
1232		if( $P.tp_class != TP_CLASS_0 ) {
1233
1234			if ( !$$.e_fcc_present ) {
1235				/* send ACK with FCC */
1236				IncStat( ts_ackreason[_ACK_FCC_] );
1237				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
1238			}
1239			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1240		}
1241	}
1242;
1243
1244/* NBS(47) */
1245	/* goes in at *** */
1246		/* just so happens that this is never true now, because we allow
1247		 * only 1 packet in the queue at once (this could be changed)
1248		if	( $P.tp_Xsnd.sb_mb )  {
1249			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
1250
1251			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1252			$P.tp_retrans = $P.tp_Nretrans;
1253			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1254			SEQ_INC($P, $P.tp_Xsndnxt);
1255		}
1256		 */
1257	/* end of the above hack */
1258
1259/* TP4 only */
1260SAME			<== 	TP_OPEN										XAK_TPDU
1261	( tp_goodXack($P, $$.e_seq) )
1262	/* tp_goodXack checks for good ack, removes the correct
1263	 * tpdu from the queue and  returns 1 if ack was legit, 0 if not.
1264	 * also updates tp_Xuna
1265	 */
1266	{
1267		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1268		tp_cuntimeout($P.tp_refp, TM_retrans);
1269
1270		sbwakeup( &$P.tp_sock->so_snd );
1271
1272		/* resume normal data */
1273		tp_send($P);
1274	}
1275;
1276
1277/* TP4, and TP0 after sending a CC or possibly a CR */
1278SAME			<==		TP_OPEN 			 						XAK_TPDU
1279	DEFAULT
1280	{
1281		IFTRACE(D_ACKRECV)
1282			tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
1283		ENDTRACE
1284		if( $P.tp_class != TP_CLASS_0 ) {
1285			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1286		}
1287	}
1288;
1289
1290/* TP4 only */
1291SAME			<==		TP_OPEN 								TM_sendack
1292	DEFAULT
1293	{
1294		IFTRACE(D_TIMER)
1295			tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
1296			$P.tp_sent_lcdt, 0);
1297		ENDTRACE
1298		IncPStat($P, tps_n_TMsendack);
1299		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1300	}
1301;
1302
1303/* TP0 only */
1304SAME			<==		TP_OPEN 									T_USR_rcvd
1305	($P.tp_class == TP_CLASS_0)
1306	{
1307		if (sbspace(&$P.tp_sock->so_rcv) > 0)
1308			tp0_openflow($P);
1309	}
1310;
1311
1312/* TP4 only */
1313		/* If old credit was zero,
1314		 * we'd better inform other side that we now have space
1315		 * But this is not enough.  Sender might not yet have
1316		 * seen an ack with cdt 0 but it might still think the
1317		 * window is closed, so it's going to wait.
1318		 * Best to send an ack each time.
1319		 * Strictly speaking, this ought to be a function of the
1320		 * general ack strategy.
1321		 */
1322SAME			<==		TP_OPEN 									T_USR_rcvd
1323	DEFAULT
1324	{
1325		if( trick_hc ) {
1326			IncStat(ts_ackreason[_ACK_USRRCV_]);
1327
1328			/* send an ACK only if there's new information */
1329			LOCAL_CREDIT( $P );
1330			if (($P.tp_rcvnxt != $P.tp_sent_rcvnxt) ||
1331				($P.tp_lcredit != $P.tp_sent_lcdt))
1332
1333				return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1334		}
1335	}
1336;
1337
1338/* applicable in TP4, TP0 */
1339SAME			<==		TP_REFWAIT 				[ T_USR_rcvd, T_USR_Xrcvd ]
1340	DEFAULT
1341	/* This happens if other end sent a DR when  the user was waiting
1342	 * on a receive.
1343	 * Processing the DR includes putting us in REFWAIT state.
1344	 */
1345	{
1346		if(trick_hc)
1347		return ECONNABORTED;
1348	}
1349;
1350
1351/* TP0 only */
1352TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT, TP_LISTENING ] 	T_NETRESET
1353	( $P.tp_class != TP_CLASS_4 )
1354		/* 0 or (4 and 0) */
1355		/* in OPEN class will be 0 or 4 but not both */
1356		/* in CRSENT or LISTENING it could be in negotiation, hence both */
1357		/* Actually, this shouldn't ever happen in LISTENING */
1358	{
1359		ASSERT( $P.tp_state != TP_LISTENING );
1360		tp_indicate(T_DISCONNECT, $P, ECONNRESET);
1361		tp_soisdisconnected($P);
1362	}
1363;
1364
1365/* TP4: ignore resets */
1366SAME		<==		[ TP_OPEN, TP_CRSENT, TP_AKWAIT,
1367						TP_CLOSING, TP_LISTENING ] 				T_NETRESET
1368	DEFAULT
1369	NULLACTION
1370;
1371
1372/* applicable in TP4, TP0 */
1373SAME			<==		[ TP_CLOSED, TP_REFWAIT ]				T_NETRESET
1374	DEFAULT
1375	NULLACTION
1376;
1377
1378/* C'EST TOUT */
1379