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