xref: /csrg-svn/sys/netiso/tp.trans (revision 38841)
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.3 (Berkeley) 08/29/89 */
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			/*$P.tp_flags |= TPF_CONN_DATA_IN;*/
319			$$.e_data = MNULL;
320		}
321	}
322;
323
324TP_OPEN		<==		 TP_CONFIRMING  								T_ACPT_req
325	( $P.tp_class == TP_CLASS_0 )
326	{
327		IncStat(ts_tp0_conn);
328		IFTRACE(D_CONN)
329			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
330		ENDTRACE
331		IFDEBUG(D_CONN)
332			printf("Confirming connection: $P" );
333		ENDDEBUG
334		soisconnected($P.tp_sock);
335		(void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ;
336		$P.tp_fcredit = 1;
337	}
338;
339
340TP_AKWAIT		<==		 TP_CONFIRMING  							T_ACPT_req
341	(tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0)
342	{
343		IncStat(ts_tp4_conn); /* even though not quite open */
344		IFTRACE(D_CONN)
345			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
346		ENDTRACE
347		IFDEBUG(D_CONN)
348			printf("Confirming connection: $P" );
349		ENDDEBUG
350		soisconnecting($P.tp_sock);
351		if($P.tp_rx_strat & TPRX_FASTSTART)
352			$P.tp_cong_win = $P.tp_fcredit;
353		$P.tp_retrans = $P.tp_Nretrans;
354		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
355	}
356;
357
358/* TP4 only */
359TP_CLOSED		<==		 TP_CONFIRMING								T_ACPT_req
360	DEFAULT /* emit failed */
361	{
362		register struct tp_ref *r = $P.tp_refp;
363
364		IFDEBUG(D_CONN)
365			printf("event: CR_TPDU emit CC failed done " );
366		ENDDEBUG
367		soisdisconnected($P.tp_sock);
368		tp_recycle_tsuffix( $P );
369		tp_freeref(r);
370		tp_detach($P);
371	}
372;
373
374/* applicable in TP4, TP0 */
375TP_CRSENT		<==		TP_CLOSED								T_CONN_req
376	DEFAULT
377	{
378		int error;
379		struct mbuf *data = MNULL;
380
381		IFTRACE(D_CONN)
382			tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
383			$P.tp_ucddata, 0, 0);
384		ENDTRACE
385		data =  MCPY($P.tp_ucddata, M_WAIT);
386		if (data) {
387			IFDEBUG(D_CONN)
388				printf("T_CONN_req.trans m_copy cc 0x%x\n",
389					$P.tp_ucddata);
390				dump_mbuf(data, "sosnd @ T_CONN_req");
391			ENDDEBUG
392		}
393
394		if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
395			return error; /* driver WON'T change state; will return error */
396
397		$P.tp_refp->tpr_state = REF_OPEN; /* has timers */
398		if($P.tp_class != TP_CLASS_0) {
399			$P.tp_retrans = $P.tp_Nretrans;
400			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
401		}
402	}
403;
404
405/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
406TP_REFWAIT 		<==		[ TP_CRSENT, TP_AKWAIT, TP_OPEN ] 			DR_TPDU
407	DEFAULT
408	{
409		if ($$.e_datalen > 0 && $P.tp_class != TP_CLASS_0) {
410			/*sbdrop(&$P.tp_Xrcv, $P.tp_Xrcv.sb_cc); /* purge expedited data */
411			sbflush(&$P.tp_Xrcv);
412			$P.tp_flags |= TPF_DISC_DATA_IN;
413			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
414			$$.e_data = MNULL;
415		}
416		tp_indicate(T_DISCONNECT, $P, TP_ERROR_MASK | (u_short)$$.e_reason);
417		tp_soisdisconnected($P);
418		if ($P.tp_class != TP_CLASS_0) {
419			if ($P.tp_state == TP_OPEN ) {
420				tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
421				tp_cuntimeout($P.tp_refp, TM_retrans);
422				tp_cuntimeout($P.tp_refp, TM_inact);
423				tp_cuntimeout($P.tp_refp, TM_sendack);
424			}
425			tp_cuntimeout($P.tp_refp, TM_retrans);
426			if( $$.e_sref !=  0 )
427				(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
428		}
429	}
430;
431
432SAME 			<==		TP_CLOSED 									DR_TPDU
433	DEFAULT
434	{
435		if( $$.e_sref != 0 )
436			(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
437		/* reference timer already set - reset it to be safe (???) */
438		tp_euntimeout($P.tp_refp, TM_reference); /* all */
439		tp_etimeout($P.tp_refp, TM_reference, 0, 0, 0, (int)$P.tp_refer_ticks);
440	}
441;
442
443/* NBS(34) */
444TP_REFWAIT 		<==  	TP_CRSENT  									ER_TPDU
445	DEFAULT
446	{
447		tp_cuntimeout($P.tp_refp, TM_retrans);
448		tp_indicate(T_DISCONNECT, $P,
449			TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
450		tp_soisdisconnected($P);
451	}
452;
453
454/* NBS(27) */
455TP_REFWAIT		<==		TP_CLOSING									DR_TPDU
456	DEFAULT
457	{
458		$P.tp_sock->so_error = (u_short)$$.e_reason;
459		tp_cuntimeout($P.tp_refp, TM_retrans);
460		tp_soisdisconnected($P);
461	}
462;
463/* these two transitions are the same but can't be combined because xebec
464 * can't handle the use of $$.e_reason if they're combined
465 */
466/* NBS(27) */
467TP_REFWAIT		<==		TP_CLOSING									ER_TPDU
468	DEFAULT
469	{
470		$P.tp_sock->so_error = (u_short)$$.e_reason;
471		tp_cuntimeout($P.tp_refp, TM_retrans);
472		tp_soisdisconnected($P);
473	}
474;
475/* NBS(27) */
476TP_REFWAIT		<==		TP_CLOSING									DC_TPDU
477	DEFAULT
478	{
479		tp_cuntimeout($P.tp_refp, TM_retrans);
480		tp_soisdisconnected($P);
481	}
482;
483
484/* NBS(21) */
485SAME 			<== 	TP_CLOSED 						[ CC_TPDU, CR_TPDU ]
486	DEFAULT
487	{	/* don't ask me why we have to do this - spec says so */
488		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
489		/* don't bother with retransmissions of the DR */
490	}
491;
492
493/* NBS(34) */
494TP_REFWAIT 		<== 	TP_OPEN  				 					ER_TPDU
495	($P.tp_class == TP_CLASS_0)
496	{
497		tp_soisdisconnecting($P.tp_sock);
498		tp_indicate(T_DISCONNECT, $P,
499			TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
500
501		tp_soisdisconnected($P);
502		tp_netcmd( $P, CONN_CLOSE );
503	}
504;
505
506TP_CLOSING 		<== 	[ TP_AKWAIT, TP_OPEN ]  					ER_TPDU
507	DEFAULT
508	{
509		if ($P.tp_state == TP_OPEN) {
510			tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
511			tp_cuntimeout($P.tp_refp, TM_inact);
512			tp_cuntimeout($P.tp_refp, TM_sendack);
513		}
514		tp_soisdisconnecting($P.tp_sock);
515		tp_indicate(T_DISCONNECT, $P,
516			TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
517		$P.tp_retrans = $P.tp_Nretrans;
518		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
519		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
520	}
521;
522/* NBS(6) */
523TP_OPEN			<==		TP_CRSENT									CC_TPDU
524	($P.tp_class == TP_CLASS_0)
525	{
526		tp_cuntimeout($P.tp_refp, TM_retrans);
527		IncStat(ts_tp0_conn);
528		$P.tp_fcredit = 1;
529		soisconnected($P.tp_sock);
530	}
531;
532
533TP_OPEN			<==		TP_CRSENT									CC_TPDU
534	DEFAULT
535	{
536		IFDEBUG(D_CONN)
537			printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
538				(int)$P.tp_flags);
539		ENDDEBUG
540		IncStat(ts_tp4_conn);
541		$P.tp_fref = $$.e_sref;
542		$P.tp_fcredit = $$.e_cdt;
543		if($P.tp_rx_strat & TPRX_FASTSTART)
544			$P.tp_cong_win = $$.e_cdt;
545		tp_getoptions($P);
546		tp_cuntimeout($P.tp_refp, TM_retrans);
547		if ($P.tp_ucddata) {
548			IFDEBUG(D_CONN)
549				printf("dropping user connect data cc 0x%x\n",
550					$P.tp_ucddata->m_len);
551			ENDDEBUG
552			m_freem($P.tp_ucddata);
553			$P.tp_ucddata = 0;
554		}
555		soisconnected($P.tp_sock);
556		if ($$.e_datalen > 0) {
557			ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
558			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
559			$P.tp_flags |= TPF_CONN_DATA_IN;
560			$$.e_data = MNULL;
561		}
562
563		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
564		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
565	}
566;
567
568/* TP4 only */
569SAME			<==		TP_CRSENT									TM_retrans
570	(	$P.tp_retrans > 0 )
571	{
572		struct mbuf *data = MNULL;
573		int error;
574
575		IncStat(ts_retrans_cr);
576		data = MCPY($P.tp_ucddata, M_NOWAIT);
577		if($P.tp_ucddata) {
578			IFDEBUG(D_CONN)
579				printf("TM_retrans.trans m_copy cc 0x%x\n", data);
580				dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
581			ENDDEBUG
582			if( data == MNULL )
583				return ENOBUFS;
584		}
585
586		$P.tp_retrans --;
587		if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
588			$P.tp_sock->so_error = error;
589		}
590		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
591	}
592;
593
594/* TP4 only  */
595TP_REFWAIT		<==		TP_CRSENT									TM_retrans
596	DEFAULT /* no more CR retransmissions */
597	{
598		IncStat(ts_conn_gaveup);
599		$P.tp_sock->so_error = ETIMEDOUT;
600		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
601		tp_soisdisconnected($P);
602	}
603;
604
605/* TP4 only */
606SAME 			<==	 TP_AKWAIT											CR_TPDU
607	DEFAULT
608	/* duplicate CR (which doesn't really exist in the context of
609	 * a connectionless network layer)
610	 * Doesn't occur in class 0.
611	 */
612	{
613		int error;
614		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
615
616		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
617			$P.tp_sock->so_error = error;
618		}
619		$P.tp_retrans = $P.tp_Nretrans;
620		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
621	}
622;
623
624/* TP4 only */
625TP_OPEN			<==		TP_AKWAIT 										DT_TPDU
626	( IN_RWINDOW( $P, $$.e_seq,
627					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
628	{
629		int doack;
630
631		/*
632		 * Get rid of any confirm or connect data, so that if we
633		 * crash or close, it isn't thought of as disconnect data.
634		 */
635		if ($P.tp_ucddata) {
636			m_freem($P.tp_ucddata);
637			$P.tp_ucddata = 0;
638		}
639		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
640		tp_cuntimeout($P.tp_refp, TM_retrans);
641		soisconnected($P.tp_sock);
642		tp_getoptions($P);
643		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
644
645		/* see also next 2 transitions, if you make any changes */
646
647		doack = tp_stash($P, $E);
648		IFDEBUG(D_DATA)
649			printf("tp_stash returns %d\n",doack);
650		ENDDEBUG
651
652		if(doack) {
653			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
654			tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
655		} else
656			tp_ctimeout( $P.tp_refp, TM_sendack, (int)$P.tp_sendack_ticks);
657
658		IFDEBUG(D_DATA)
659			printf("after stash calling sbwakeup\n");
660		ENDDEBUG
661	}
662;
663
664SAME			<==		TP_OPEN 									DT_TPDU
665	( $P.tp_class == TP_CLASS_0 )
666	{
667		tp0_stash($P, $E);
668		sbwakeup( &$P.tp_sock->so_rcv );
669
670		IFDEBUG(D_DATA)
671			printf("after stash calling sbwakeup\n");
672		ENDDEBUG
673	}
674;
675
676/* TP4 only */
677SAME			<==		TP_OPEN 									DT_TPDU
678	( IN_RWINDOW( $P, $$.e_seq,
679					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
680	{
681		int doack; /* tells if we must ack immediately */
682
683		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
684		sbwakeup( &$P.tp_sock->so_rcv );
685
686		doack = tp_stash($P, $E);
687		IFDEBUG(D_DATA)
688			printf("tp_stash returns %d\n",doack);
689		ENDDEBUG
690
691		if(doack)
692			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
693		else
694			tp_ctimeout_MIN( $P.tp_refp, TM_sendack, (int)$P.tp_sendack_ticks);
695
696		IFDEBUG(D_DATA)
697			printf("after stash calling sbwakeup\n");
698		ENDDEBUG
699	}
700;
701
702/* Not in window  - we must ack under certain circumstances, namely
703 * a) if the seq number is below lwe but > lwe - (max credit ever given)
704 * (to handle lost acks) Can use max-possible-credit for this ^^^.
705 * and
706 * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
707 *
708 * (see 12.2.3.8.1 of ISO spec, p. 73)
709 * We just always ack.
710 */
711/* TP4 only */
712SAME 			<== 	[ TP_OPEN, TP_AKWAIT ]							DT_TPDU
713	DEFAULT /* Not in window */
714	{
715		IFTRACE(D_DATA)
716			tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
717				$$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
718		ENDTRACE
719		IncStat(ts_dt_niw);
720		m_freem($$.e_data);
721		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
722		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
723	}
724;
725
726/* TP4 only */
727TP_OPEN			<==		TP_AKWAIT										AK_TPDU
728	DEFAULT
729	{
730		if ($P.tp_ucddata) {
731			m_freem($P.tp_ucddata);
732			$P.tp_ucddata = 0;
733		}
734		(void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
735		tp_cuntimeout($P.tp_refp, TM_retrans);
736
737		tp_getoptions($P);
738		soisconnected($P.tp_sock);
739		IFTRACE(D_CONN)
740			struct socket *so = $P.tp_sock;
741			tptrace(TPPTmisc,
742			"called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
743				so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
744			tptrace(TPPTmisc,
745			"called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
746				so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
747		ENDTRACE
748
749		tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
750		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
751	}
752;
753
754/* TP4 only */
755TP_OPEN 		<== 	[ TP_OPEN, TP_AKWAIT ]						XPD_TPDU
756	( $P.tp_Xrcvnxt == $$.e_seq  /* && $P.tp_Xrcv.sb_cc == 0*/)
757	{
758		if( $P.tp_state == TP_AKWAIT ) {
759			if ($P.tp_ucddata) {
760				m_freem($P.tp_ucddata);
761				$P.tp_ucddata = 0;
762			}
763			tp_cuntimeout($P.tp_refp, TM_retrans);
764			tp_getoptions($P);
765			soisconnected($P.tp_sock);
766			tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
767			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
768		}
769		IFTRACE(D_XPD)
770		tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
771				$P.tp_Xrcvnxt,$$.e_seq,  $$.e_datalen, $$.e_data->m_len);
772		ENDTRACE
773
774		$P.tp_sock->so_state |= SS_RCVATMARK;
775		sbinsertoob(&$P.tp_Xrcv, $$.e_data);
776		IFDEBUG(D_XPD)
777			dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
778		ENDDEBUG
779		tp_indicate(T_XDATA, $P, 0);
780		sbwakeup( &$P.tp_Xrcv );
781
782		(void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
783		SEQ_INC($P, $P.tp_Xrcvnxt);
784	}
785;
786
787/* TP4 only */
788SAME			<==		TP_OPEN 									T_USR_Xrcvd
789	DEFAULT
790	{
791		if( $P.tp_Xrcv.sb_cc == 0 ) {
792			/*$P.tp_flags &= ~TPF_XPD_PRESENT;*/
793			/* kludge for select(): */
794			/* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
795		}
796	}
797	/* OLD WAY:
798	 * Ack only after the user receives the XPD.  This is better for
799	 * users that use one XPD right after another.
800	 * Acking right away (the NEW WAY, see the prev. transition) is
801	 * better for occasional * XPD, when the receiving user doesn't
802	 * want to read the XPD immediately (which is session's behavior).
803	 *
804		int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
805		SEQ_INC($P, $P.tp_Xrcvnxt);
806		return error;
807	*/
808;
809
810/* NOTE: presently if the user doesn't read the connection data
811 * before and expedited data PDU comes in, the connection data will
812 * be dropped. This is a bug.  To avoid it, we need somewhere else
813 * to put the connection data.
814 * On the other hand, we need not to have it sitting around forever.
815 * This is a problem with the idea of trying to accommodate
816 * data on connect w/ a passive-open user interface.
817 */
818/* TP4 only */
819
820SAME	 		<== 	[ TP_AKWAIT, TP_OPEN ] 							XPD_TPDU
821	DEFAULT /* not in window or cdt==0 */
822	{
823		IFTRACE(D_XPD)
824			tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
825				$P.tp_Xrcvnxt, $$.e_seq,  $P.tp_Xrcv.sb_cc , 0);
826		ENDTRACE
827		if( $P.tp_Xrcvnxt != $$.e_seq )
828			IncStat(ts_xpd_niw);
829		if( $P.tp_Xrcv.sb_cc ) {
830#ifdef notdef
831			if( $P.tp_flags & TPF_CONN_DATA_IN ) {
832				/* user isn't reading the connection data; see note above */
833				sbdrop(&$P.tp_Xrcv, $P.tp_Xrcv.sb_cc);
834				$P.tp_flags &= ~TPF_CONN_DATA_IN;
835			}
836#endif notdef
837			/* might as well kick 'em again */
838			tp_indicate(T_XDATA, $P, 0);
839			IncStat(ts_xpd_dup);
840		}
841		m_freem($$.e_data);
842		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
843		/* don't send an xack because the xak gives "last one received", not
844		 * "next one i expect" (dumb)
845		 */
846	}
847;
848
849/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
850 * to detach all its "children"
851 * Also (CRSENT) when user kills a job that's doing a connect()
852 */
853TP_REFWAIT		<== 	TP_CRSENT 										T_DETACH
854	($P.tp_class == TP_CLASS_0)
855	{
856		struct socket *so = $P.tp_sock;
857
858		/* detach from parent socket so it can finish closing */
859		if (so->so_head) {
860			if (!soqremque(so, 0) && !soqremque(so, 1))
861				panic("tp: T_DETACH");
862			so->so_head = 0;
863		}
864		tp_soisdisconnecting($P.tp_sock);
865		tp_netcmd( $P, CONN_CLOSE);
866		tp_soisdisconnected($P);
867	}
868;
869
870/* TP4 only */
871TP_CLOSING		<== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ]	T_DETACH
872	DEFAULT
873	{
874		struct socket *so = $P.tp_sock;
875		struct mbuf *data = MNULL;
876
877		/* detach from parent socket so it can finish closing */
878		if (so->so_head) {
879			if (!soqremque(so, 0) && !soqremque(so, 1))
880				panic("tp: T_DETACH");
881			so->so_head = 0;
882		}
883		if ($P.tp_state != TP_CLOSING) {
884			tp_soisdisconnecting($P.tp_sock);
885			data = MCPY($P.tp_ucddata, M_NOWAIT);
886			(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
887			$P.tp_retrans = $P.tp_Nretrans;
888			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
889		}
890	}
891;
892
893TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT ]		 	  			T_DISC_req
894	( $P.tp_class == TP_CLASS_0 )
895	{
896		tp_soisdisconnecting($P.tp_sock);
897		tp_netcmd( $P, CONN_CLOSE);
898		tp_soisdisconnected($P);
899	}
900;
901
902/* TP4 only */
903TP_CLOSING		<==	[ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ]  T_DISC_req
904	DEFAULT
905	{
906		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
907
908		if($P.tp_state == TP_OPEN) {
909			tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
910			tp_cuntimeout($P.tp_refp, TM_inact);
911			tp_cuntimeout($P.tp_refp, TM_sendack);
912		}
913		if (data) {
914			IFDEBUG(D_CONN)
915				printf("T_DISC_req.trans tp_ucddata 0x%x\n",
916					$P.tp_ucddata);
917				dump_mbuf(data, "ucddata @ T_DISC_req");
918			ENDDEBUG
919		}
920		tp_soisdisconnecting($P.tp_sock);
921		$P.tp_retrans = $P.tp_Nretrans;
922		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
923
924		if( trick_hc )
925			return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
926	}
927;
928
929/* TP4 only */
930SAME			<==		TP_AKWAIT									TM_retrans
931	( $P.tp_retrans > 0 )
932	{
933		int error;
934		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
935
936		IncStat(ts_retrans_cc);
937		$P.tp_retrans --;
938		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
939			$P.tp_sock->so_error = error;
940		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
941	}
942;
943
944/* TP4 only */
945TP_CLOSING		<==		TP_AKWAIT									TM_retrans
946	DEFAULT  /* out of time */
947	{
948		IncStat(ts_conn_gaveup);
949		tp_soisdisconnecting($P.tp_sock);
950		$P.tp_sock->so_error = ETIMEDOUT;
951		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
952		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
953		$P.tp_retrans = $P.tp_Nretrans;
954		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
955	}
956;
957
958/* the retrans timers had better go off BEFORE the inactivity timer does,
959 * if transmissions are going on.
960 * (i.e., TM_inact should be greater than timer for all retrans plus ack
961 * turnaround)
962 */
963/* TP4 only */
964TP_CLOSING 		<==		TP_OPEN		   [ TM_inact, TM_retrans, TM_data_retrans ]
965	DEFAULT
966	{
967		tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
968		tp_cuntimeout($P.tp_refp, TM_inact);
969		tp_cuntimeout($P.tp_refp, TM_sendack);
970
971		IncStat(ts_conn_gaveup);
972		tp_soisdisconnecting($P.tp_sock);
973		$P.tp_sock->so_error = ETIMEDOUT;
974		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
975		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
976		$P.tp_retrans = $P.tp_Nretrans;
977		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
978	}
979;
980
981/* TP4 only */
982SAME			<==		TP_OPEN										TM_retrans
983	( $P.tp_retrans > 0 )
984	{
985		/* resume XPD */
986		if	( $P.tp_Xsnd.sb_mb )  {
987			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
988			/* m_copy doesn't preserve the m_xlink field, but at this pt.
989			 * that doesn't matter
990			 */
991
992			IFTRACE(D_XPD)
993				tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndhiwat snduna",
994					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
995					$P.tp_snduna);
996			ENDTRACE
997			IFDEBUG(D_XPD)
998				dump_mbuf(m, "XPD retrans emitting M");
999			ENDDEBUG
1000			IncStat(ts_retrans_xpd);
1001			$P.tp_retrans --;
1002			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1003			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1004		}
1005	}
1006;
1007
1008/* TP4 only */
1009SAME 			<==		TP_OPEN									TM_data_retrans
1010	( $$.e_retrans > 0 )
1011	{
1012		register 	SeqNum			low, lowsave = 0;
1013		register	struct tp_rtc 	*r = $P.tp_snduna_rtc;
1014		register	struct mbuf 	*m;
1015		register	SeqNum			high = $$.e_high;
1016
1017		low =
1018			SEQ_GT($P, $P.tp_snduna, $$.e_low )? $P.tp_snduna: $$.e_low;
1019		lowsave = low;
1020		if (($P.tp_rx_strat & TPRX_EACH) == 0)
1021			high = (high>low)?low:high;
1022
1023		if( $P.tp_rx_strat & TPRX_USE_CW ) {
1024			register int i;
1025
1026			$P.tp_cong_win = 1;
1027
1028			i = SEQ_ADD($P, low, $P.tp_cong_win);
1029			if(SEQ_LT($P, i, high ))
1030				high = i;
1031		}
1032
1033		while( SEQ_LEQ($P, low, high) ){
1034			if ( r == (struct tp_rtc *)0 ){
1035				IFDEBUG(D_RTC)
1036					printf( "tp: retrans rtc list is GONE!\n");
1037				ENDDEBUG
1038				break;
1039			}
1040			if ( r->tprt_seq == low ){
1041				if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))== MNULL)
1042					break;
1043				(void) tp_emit(DT_TPDU_type, $P, low, r->tprt_eot, m);
1044				IncStat(ts_retrans_dt);
1045				SEQ_INC($P, low );
1046			}
1047			r = r->tprt_next;
1048		}
1049		if ( SEQ_LEQ($P, lowsave, high) ){
1050			$$.e_retrans --;
1051			tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)lowsave,
1052					(caddr_t)high, $$.e_retrans,
1053					($P.tp_Nretrans - $$.e_retrans) * (int)$P.tp_dt_ticks);
1054		}
1055	}
1056;
1057
1058/* TP4 only */
1059SAME	 		<==		TP_CLOSING									TM_retrans
1060	(	$P.tp_retrans > 0 )
1061	{
1062		$P.tp_retrans --;
1063		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
1064		IncStat(ts_retrans_dr);
1065		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
1066	}
1067;
1068
1069/* TP4 only */
1070TP_REFWAIT 		<==		TP_CLOSING									TM_retrans
1071	DEFAULT	/* no more retrans - gave up */
1072	{
1073		$P.tp_sock->so_error = ETIMEDOUT;
1074		$P.tp_refp->tpr_state = REF_FROZEN;
1075		tp_recycle_tsuffix( $P );
1076		tp_etimeout($P.tp_refp, TM_reference, 0,0,0, (int)$P.tp_refer_ticks);
1077	}
1078;
1079
1080/*
1081 * The resources are kept around until the ref timer goes off.
1082 * The suffices are wiped out sooner so they can be reused right away.
1083 */
1084/* applicable in TP4, TP0 */
1085TP_CLOSED 		<==		TP_REFWAIT 									TM_reference
1086	DEFAULT
1087	{
1088		tp_freeref($P.tp_refp);
1089		tp_detach($P);
1090	}
1091;
1092
1093/* applicable in TP4, TP0 */
1094/* A duplicate CR from connectionless network layer can't happen */
1095SAME 			<== 	TP_OPEN 							[ CR_TPDU, CC_TPDU ]
1096	DEFAULT
1097	{
1098		if( $P.tp_class != TP_CLASS_0) {
1099			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1100			if ( $E.ev_number == CC_TPDU )
1101				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1102		}
1103		/* ignore it if class 0 - state tables are blank for this */
1104	}
1105;
1106
1107/* applicable in TP4, TP0 */
1108SAME			<== 	TP_OPEN									T_DATA_req
1109	DEFAULT
1110	{
1111		IFTRACE(D_DATA)
1112			tptrace(TPPTmisc, "T_DATA_req sndhiwat snduna fcredit, tpcb",
1113				$P.tp_sndhiwat, $P.tp_snduna, $P.tp_fcredit, $P);
1114		ENDTRACE
1115
1116		tp_send($P);
1117	}
1118;
1119
1120/* TP4 only */
1121SAME			<==		TP_OPEN										T_XPD_req
1122	DEFAULT
1123		/* T_XPD_req was issued by sosend iff xpd socket buf was empty
1124		 * at time of sosend(),
1125		 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
1126		 */
1127	{
1128		int error = 0;
1129
1130		/* resume XPD */
1131		if	( $P.tp_Xsnd.sb_mb )  {
1132			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
1133			/* m_copy doesn't preserve the m_xlink field, but at this pt.
1134			 * that doesn't matter
1135			 */
1136
1137			IFTRACE(D_XPD)
1138				tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndhiwat snduna",
1139					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
1140					$P.tp_snduna);
1141			ENDTRACE
1142			IFDEBUG(D_XPD)
1143				printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
1144				dump_mbuf(m, "XPD req emitting M");
1145			ENDDEBUG
1146			error =
1147				tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1148			$P.tp_retrans = $P.tp_Nretrans;
1149			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1150			SEQ_INC($P, $P.tp_Xsndnxt);
1151		}
1152		if(trick_hc)
1153			return error;
1154	}
1155;
1156
1157/* TP4, faked ack in TP0 when cons send completes */
1158SAME 			<==		TP_OPEN 									AK_TPDU
1159	( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq)  )
1160
1161	/* tp_goodack == true means
1162	 * EITHER it actually acked something heretofore unacknowledged
1163	 * OR no news but the credit should be processed.
1164	 */
1165	{
1166		IFDEBUG(D_ACKRECV)
1167			printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
1168		ENDDEBUG
1169		if( $P.tp_class != TP_CLASS_0) {
1170			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1171			tp_euntimeout_lss($P.tp_refp, TM_data_retrans, $$.e_seq);
1172		}
1173		sbwakeup( &$P.tp_sock->so_snd );
1174
1175		tp_send($P);
1176		IFDEBUG(D_ACKRECV)
1177			printf("GOOD ACK new sndhiwat 0x%x\n", $P.tp_sndhiwat);
1178		ENDDEBUG
1179	}
1180;
1181
1182/* TP4, and TP0 after sending a CC or possibly a CR */
1183SAME			<==		TP_OPEN 			 						XAK_TPDU
1184	DEFAULT
1185	{
1186		IFTRACE(D_ACKRECV)
1187			tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
1188		ENDTRACE
1189		if( $P.tp_class != TP_CLASS_0 ) {
1190			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1191		}
1192	}
1193;
1194/* TP4, and TP0 after sending a CC or possibly a CR */
1195SAME			<==		TP_OPEN 			 						 AK_TPDU
1196	DEFAULT
1197	{
1198		IFTRACE(D_ACKRECV)
1199			tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
1200				$$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
1201		ENDTRACE
1202		if( $P.tp_class != TP_CLASS_0 ) {
1203
1204			if ( !$$.e_fcc_present ) {
1205				/* send ACK with FCC */
1206				IncStat( ts_ackreason[_ACK_FCC_] );
1207				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
1208			}
1209			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1210		}
1211	}
1212;
1213
1214/* NBS(47) */
1215	/* goes in at *** */
1216		/* just so happens that this is never true now, because we allow
1217		 * only 1 packet in the queue at once (this could be changed)
1218		if	( $P.tp_Xsnd.sb_mb )  {
1219			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
1220
1221			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1222			$P.tp_retrans = $P.tp_Nretrans;
1223			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1224			SEQ_INC($P, $P.tp_Xsndnxt);
1225		}
1226		 */
1227	/* end of the above hack */
1228
1229/* TP4 only */
1230SAME			<== 	TP_OPEN 										XAK_TPDU
1231	( tp_goodXack($P, $$.e_seq) )
1232	/* tp_goodXack checks for good ack, removes the correct
1233	 * tpdu from the queue and  returns 1 if ack was legit, 0 if not.
1234	 * also updates tp_Xuna
1235	 */
1236	{
1237		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1238		tp_cuntimeout($P.tp_refp, TM_retrans);
1239
1240		sbwakeup( &$P.tp_sock->so_snd );
1241
1242		/* resume normal data */
1243		tp_send($P);
1244	}
1245;
1246
1247/* TP4 only */
1248SAME			<==		TP_OPEN 								TM_sendack
1249	DEFAULT
1250	{
1251		IFTRACE(D_TIMER)
1252			tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
1253			$P.tp_sent_lcdt, 0);
1254		ENDTRACE
1255		IncPStat($P, tps_n_TMsendack);
1256		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1257	}
1258;
1259
1260/* TP0 only */
1261SAME			<==		TP_OPEN 									T_USR_rcvd
1262	($P.tp_class == TP_CLASS_0)
1263	NULLACTION
1264;
1265
1266/* TP4 only */
1267		/* If old credit was zero,
1268		 * we'd better inform other side that we now have space
1269		 * But this is not enough.  Sender might not yet have
1270		 * seen an ack with cdt 0 but it might still think the
1271		 * window is closed, so it's going to wait.
1272		 * Best to send an ack each time.
1273		 * Strictly speaking, this ought to be a function of the
1274		 * general ack strategy.
1275		 */
1276SAME			<==		TP_OPEN 									T_USR_rcvd
1277	DEFAULT
1278	{
1279		if( trick_hc ) {
1280			IncStat(ts_ackreason[_ACK_USRRCV_]);
1281			return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1282		}
1283	}
1284;
1285
1286/* applicable in TP4, TP0 */
1287SAME			<==		TP_REFWAIT 				[ T_USR_rcvd, T_USR_Xrcvd ]
1288	DEFAULT
1289	/* This happens if other end sent a DR when  the user was waiting
1290	 * on a receive.
1291	 * Processing the DR includes putting us in REFWAIT state.
1292	 */
1293	{
1294		if(trick_hc)
1295		return ECONNABORTED;
1296	}
1297;
1298
1299/* TP0 only */
1300TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT, TP_LISTENING ] 	T_NETRESET
1301	( $P.tp_class != TP_CLASS_4 )
1302		/* 0 or (4 and 0) */
1303		/* in OPEN class will be 0 or 4 but not both */
1304		/* in CRSENT or LISTENING it could be in negotiation, hence both */
1305		/* Actually, this shouldn't ever happen in LISTENING */
1306	{
1307		ASSERT( $P.tp_state != TP_LISTENING );
1308		tp_indicate(T_DISCONNECT, $P, ECONNRESET);
1309		tp_soisdisconnected($P);
1310	}
1311;
1312
1313/* TP4: ignore resets */
1314SAME		<==		[ TP_OPEN, TP_CRSENT, TP_AKWAIT,
1315						TP_CLOSING, TP_LISTENING ] 				T_NETRESET
1316	DEFAULT
1317	NULLACTION
1318;
1319
1320/* applicable in TP4, TP0 */
1321SAME			<==		[ TP_CLOSED, TP_REFWAIT ]				T_NETRESET
1322	DEFAULT
1323	NULLACTION
1324;
1325
1326/* C'EST TOUT */
1327