xref: /original-bsd/sys/netiso/tp.trans (revision 1e29b3fc)
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.19 (Berkeley) 05/27/92
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.19 (Berkeley) 05/27/92 */
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				$P.tp_flags &= ~TPF_DELACK;
439			}
440			tp_cuntimeout($P, TM_retrans);
441			if( $$.e_sref !=  0 )
442				(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
443		}
444	}
445;
446
447SAME 			<==		TP_CLOSED 									DR_TPDU
448	DEFAULT
449	{
450		if( $$.e_sref != 0 )
451			(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
452		/* reference timer already set - reset it to be safe (???) */
453		tp_euntimeout($P, TM_reference); /* all */
454		tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
455	}
456;
457
458/* NBS(34) */
459TP_REFWAIT 		<==  	TP_CRSENT  									ER_TPDU
460	DEFAULT
461	{
462		tp_cuntimeout($P, TM_retrans);
463		tp_indicate(ER_TPDU, $P, $$.e_reason);
464		tp_soisdisconnected($P);
465	}
466;
467
468/* NBS(27) */
469TP_REFWAIT		<==		TP_CLOSING									DR_TPDU
470	DEFAULT
471	{
472		tp_cuntimeout($P, TM_retrans);
473		tp_soisdisconnected($P);
474	}
475;
476/* these two transitions are the same but can't be combined because xebec
477 * can't handle the use of $$.e_reason if they're combined
478 */
479/* NBS(27) */
480TP_REFWAIT		<==		TP_CLOSING									ER_TPDU
481	DEFAULT
482	{
483		tp_indicate(ER_TPDU, $P, $$.e_reason);
484		tp_cuntimeout($P, TM_retrans);
485		tp_soisdisconnected($P);
486	}
487;
488/* NBS(27) */
489TP_REFWAIT		<==		TP_CLOSING									DC_TPDU
490	DEFAULT
491	{
492		tp_cuntimeout($P, TM_retrans);
493		tp_soisdisconnected($P);
494	}
495;
496
497/* NBS(21) */
498SAME 			<== 	TP_CLOSED 						[ CC_TPDU, CR_TPDU ]
499	DEFAULT
500	{	/* don't ask me why we have to do this - spec says so */
501		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
502		/* don't bother with retransmissions of the DR */
503	}
504;
505
506/* NBS(34) */
507TP_REFWAIT 		<== 	TP_OPEN  				 					ER_TPDU
508	($P.tp_class == TP_CLASS_0)
509	{
510		tp_soisdisconnecting($P.tp_sock);
511		tp_indicate(ER_TPDU, $P, $$.e_reason);
512		tp_soisdisconnected($P);
513		tp_netcmd( $P, CONN_CLOSE );
514	}
515;
516
517TP_CLOSING 		<== 	[ TP_AKWAIT, TP_OPEN ]  					ER_TPDU
518	DEFAULT
519	{
520		if ($P.tp_state == TP_OPEN) {
521			tp_euntimeout($P, TM_data_retrans); /* all */
522			tp_cuntimeout($P, TM_inact);
523			tp_cuntimeout($P, TM_sendack);
524		}
525		tp_soisdisconnecting($P.tp_sock);
526		tp_indicate(ER_TPDU, $P, $$.e_reason);
527		$P.tp_retrans = $P.tp_Nretrans;
528		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
529		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
530	}
531;
532/* NBS(6) */
533TP_OPEN			<==		TP_CRSENT									CC_TPDU
534	($P.tp_class == TP_CLASS_0)
535	{
536		tp_cuntimeout($P, TM_retrans);
537		IncStat(ts_tp0_conn);
538		$P.tp_fcredit = 1;
539		soisconnected($P.tp_sock);
540	}
541;
542
543TP_OPEN			<==		TP_CRSENT									CC_TPDU
544	DEFAULT
545	{
546		IFDEBUG(D_CONN)
547			printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
548				(int)$P.tp_flags);
549		ENDDEBUG
550		IncStat(ts_tp4_conn);
551		$P.tp_fref = $$.e_sref;
552		$P.tp_fcredit = $$.e_cdt;
553		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0))
554			$P.tp_cong_win = $$.e_cdt * $P.tp_l_tpdusize;
555		tp_getoptions($P);
556		tp_cuntimeout($P, TM_retrans);
557		if ($P.tp_ucddata) {
558			IFDEBUG(D_CONN)
559				printf("dropping user connect data cc 0x%x\n",
560					$P.tp_ucddata->m_len);
561			ENDDEBUG
562			m_freem($P.tp_ucddata);
563			$P.tp_ucddata = 0;
564		}
565		soisconnected($P.tp_sock);
566		if ($$.e_datalen > 0) {
567			ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
568			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
569			$$.e_data = MNULL;
570		}
571
572		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
573		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
574	}
575;
576
577/* TP4 only */
578SAME			<==		TP_CRSENT									TM_retrans
579	(	$P.tp_retrans > 0 )
580	{
581		struct mbuf *data = MNULL;
582		int error;
583
584		IncStat(ts_retrans_cr);
585		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
586		data = MCPY($P.tp_ucddata, M_NOWAIT);
587		if($P.tp_ucddata) {
588			IFDEBUG(D_CONN)
589				printf("TM_retrans.trans m_copy cc 0x%x\n", data);
590				dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
591			ENDDEBUG
592			if( data == MNULL )
593				return ENOBUFS;
594		}
595
596		$P.tp_retrans --;
597		if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
598			$P.tp_sock->so_error = error;
599		}
600		tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
601	}
602;
603
604/* TP4 only  */
605TP_REFWAIT		<==		TP_CRSENT									TM_retrans
606	DEFAULT /* no more CR retransmissions */
607	{
608		IncStat(ts_conn_gaveup);
609		$P.tp_sock->so_error = ETIMEDOUT;
610		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
611		tp_soisdisconnected($P);
612	}
613;
614
615/* TP4 only */
616SAME 			<==	 TP_AKWAIT											CR_TPDU
617	DEFAULT
618	/* duplicate CR (which doesn't really exist in the context of
619	 * a connectionless network layer)
620	 * Doesn't occur in class 0.
621	 */
622	{
623		int error;
624		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
625
626		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
627			$P.tp_sock->so_error = error;
628		}
629		$P.tp_retrans = $P.tp_Nretrans;
630		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
631	}
632;
633
634/* TP4 only */
635TP_OPEN			<==		TP_AKWAIT 										DT_TPDU
636	( IN_RWINDOW( $P, $$.e_seq,
637					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
638	{
639		int doack;
640
641		/*
642		 * Get rid of any confirm or connect data, so that if we
643		 * crash or close, it isn't thought of as disconnect data.
644		 */
645		if ($P.tp_ucddata) {
646			m_freem($P.tp_ucddata);
647			$P.tp_ucddata = 0;
648		}
649		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
650		tp_cuntimeout($P, TM_retrans);
651		soisconnected($P.tp_sock);
652		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
653
654		/* see also next 2 transitions, if you make any changes */
655
656		doack = tp_stash($P, $E);
657		IFDEBUG(D_DATA)
658			printf("tp_stash returns %d\n",doack);
659		ENDDEBUG
660
661		if (doack) {
662			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
663			tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
664		} else
665			tp_ctimeout( $P, TM_sendack, (int)$P.tp_sendack_ticks);
666
667		IFDEBUG(D_DATA)
668			printf("after stash calling sbwakeup\n");
669		ENDDEBUG
670	}
671;
672
673SAME			<==		TP_OPEN 									DT_TPDU
674	( $P.tp_class == TP_CLASS_0 )
675	{
676		tp0_stash($P, $E);
677		sbwakeup( &$P.tp_sock->so_rcv );
678
679		IFDEBUG(D_DATA)
680			printf("after stash calling sbwakeup\n");
681		ENDDEBUG
682	}
683;
684
685/* TP4 only */
686SAME			<==		TP_OPEN 									DT_TPDU
687	( IN_RWINDOW( $P, $$.e_seq,
688					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
689	{
690		int doack; /* tells if we must ack immediately */
691
692		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
693		sbwakeup( &$P.tp_sock->so_rcv );
694
695		doack = tp_stash($P, $E);
696		IFDEBUG(D_DATA)
697			printf("tp_stash returns %d\n",doack);
698		ENDDEBUG
699
700		if(doack)
701			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
702		else
703			tp_ctimeout_MIN( $P, TM_sendack, (int)$P.tp_sendack_ticks);
704
705		IFDEBUG(D_DATA)
706			printf("after stash calling sbwakeup\n");
707		ENDDEBUG
708	}
709;
710
711/* Not in window  - we must ack under certain circumstances, namely
712 * a) if the seq number is below lwe but > lwe - (max credit ever given)
713 * (to handle lost acks) Can use max-possible-credit for this ^^^.
714 * and
715 * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
716 *
717 * (see 12.2.3.8.1 of ISO spec, p. 73)
718 * We just always ack.
719 */
720/* TP4 only */
721SAME 			<== 	[ TP_OPEN, TP_AKWAIT ]							DT_TPDU
722	DEFAULT /* Not in window */
723	{
724		IFTRACE(D_DATA)
725			tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
726				$$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
727		ENDTRACE
728		IncStat(ts_dt_niw);
729		m_freem($$.e_data);
730		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
731		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
732	}
733;
734
735/* TP4 only */
736TP_OPEN			<==		TP_AKWAIT										AK_TPDU
737	DEFAULT
738	{
739		if ($P.tp_ucddata) {
740			m_freem($P.tp_ucddata);
741			$P.tp_ucddata = 0;
742		}
743		(void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
744		tp_cuntimeout($P, TM_retrans);
745
746		soisconnected($P.tp_sock);
747		IFTRACE(D_CONN)
748			struct socket *so = $P.tp_sock;
749			tptrace(TPPTmisc,
750			"called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
751				so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
752			tptrace(TPPTmisc,
753			"called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
754				so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
755		ENDTRACE
756
757		tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
758		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
759	}
760;
761
762/* TP4 only */
763TP_OPEN 		<== 	[ TP_OPEN, TP_AKWAIT ]						XPD_TPDU
764	($P.tp_Xrcvnxt == $$.e_seq)
765	{
766		if( $P.tp_state == TP_AKWAIT ) {
767			if ($P.tp_ucddata) {
768				m_freem($P.tp_ucddata);
769				$P.tp_ucddata = 0;
770			}
771			tp_cuntimeout($P, TM_retrans);
772			soisconnected($P.tp_sock);
773			tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
774			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
775		}
776		IFTRACE(D_XPD)
777		tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
778				$P.tp_Xrcvnxt,$$.e_seq,  $$.e_datalen, $$.e_data->m_len);
779		ENDTRACE
780
781		$P.tp_sock->so_state |= SS_RCVATMARK;
782		$$.e_data->m_flags |= M_EOR;
783		sbinsertoob(&$P.tp_Xrcv, $$.e_data);
784		IFDEBUG(D_XPD)
785			dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
786		ENDDEBUG
787		tp_indicate(T_XDATA, $P, 0);
788		sbwakeup( &$P.tp_Xrcv );
789
790		(void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
791		SEQ_INC($P, $P.tp_Xrcvnxt);
792	}
793;
794
795/* TP4 only */
796SAME			<==		TP_OPEN 									T_USR_Xrcvd
797	DEFAULT
798	{
799		if( $P.tp_Xrcv.sb_cc == 0 ) {
800			/* kludge for select(): */
801			/* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
802		}
803	}
804	/* OLD WAY:
805	 * Ack only after the user receives the XPD.  This is better for
806	 * users that use one XPD right after another.
807	 * Acking right away (the NEW WAY, see the prev. transition) is
808	 * better for occasional * XPD, when the receiving user doesn't
809	 * want to read the XPD immediately (which is session's behavior).
810	 *
811		int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
812		SEQ_INC($P, $P.tp_Xrcvnxt);
813		return error;
814	*/
815;
816
817/* NOTE: presently if the user doesn't read the connection data
818 * before and expedited data PDU comes in, the connection data will
819 * be dropped. This is a bug.  To avoid it, we need somewhere else
820 * to put the connection data.
821 * On the other hand, we need not to have it sitting around forever.
822 * This is a problem with the idea of trying to accommodate
823 * data on connect w/ a passive-open user interface.
824 */
825/* TP4 only */
826
827SAME	 		<== 	[ TP_AKWAIT, TP_OPEN ] 							XPD_TPDU
828	DEFAULT /* not in window or cdt==0 */
829	{
830		IFTRACE(D_XPD)
831			tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
832				$P.tp_Xrcvnxt, $$.e_seq,  $P.tp_Xrcv.sb_cc , 0);
833		ENDTRACE
834		if( $P.tp_Xrcvnxt != $$.e_seq )
835			IncStat(ts_xpd_niw);
836		if( $P.tp_Xrcv.sb_cc ) {
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, 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, 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, TM_data_retrans); /* all */
910			tp_cuntimeout($P, TM_inact);
911			tp_cuntimeout($P, TM_sendack);
912			$P.tp_flags &= ~TPF_DELACK;
913		}
914		if (data) {
915			IFDEBUG(D_CONN)
916				printf("T_DISC_req.trans tp_ucddata 0x%x\n",
917					$P.tp_ucddata);
918				dump_mbuf(data, "ucddata @ T_DISC_req");
919			ENDDEBUG
920		}
921		tp_soisdisconnecting($P.tp_sock);
922		$P.tp_retrans = $P.tp_Nretrans;
923		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
924
925		if( trick_hc )
926			return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
927	}
928;
929
930/* TP4 only */
931SAME			<==		TP_AKWAIT									TM_retrans
932	( $P.tp_retrans > 0 )
933	{
934		int error;
935		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
936
937		IncStat(ts_retrans_cc);
938		$P.tp_retrans --;
939		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
940
941		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
942			$P.tp_sock->so_error = error;
943		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
944	}
945;
946
947/* TP4 only */
948TP_CLOSING		<==		TP_AKWAIT									TM_retrans
949	DEFAULT  /* out of time */
950	{
951		IncStat(ts_conn_gaveup);
952		tp_soisdisconnecting($P.tp_sock);
953		$P.tp_sock->so_error = ETIMEDOUT;
954		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
955		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
956		$P.tp_retrans = $P.tp_Nretrans;
957		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
958	}
959;
960
961/* the retrans timers had better go off BEFORE the inactivity timer does,
962 * if transmissions are going on.
963 * (i.e., TM_inact should be greater than timer for all retrans plus ack
964 * turnaround)
965 */
966/* TP4 only */
967TP_CLOSING 		<==		TP_OPEN		   [ TM_inact, TM_retrans, TM_data_retrans ]
968	DEFAULT
969	{
970		tp_euntimeout($P, TM_data_retrans); /* all */
971		tp_cuntimeout($P, TM_inact);
972		tp_cuntimeout($P, TM_sendack);
973
974		IncStat(ts_conn_gaveup);
975		tp_soisdisconnecting($P.tp_sock);
976		$P.tp_sock->so_error = ETIMEDOUT;
977		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
978		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
979		$P.tp_retrans = $P.tp_Nretrans;
980		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
981	}
982;
983
984/* TP4 only */
985SAME			<==		TP_OPEN										TM_retrans
986	( $P.tp_retrans > 0 )
987	{
988		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
989		/* resume XPD */
990		if	( $P.tp_Xsnd.sb_mb )  {
991			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
992			int shift;
993
994			IFTRACE(D_XPD)
995				tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna",
996					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
997					$P.tp_snduna);
998			ENDTRACE
999			IFDEBUG(D_XPD)
1000				dump_mbuf(m, "XPD retrans emitting M");
1001			ENDDEBUG
1002			IncStat(ts_retrans_xpd);
1003			$P.tp_retrans --;
1004			shift = max($P.tp_Nretrans - $P.tp_retrans, 6);
1005			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1006			tp_ctimeout($P, TM_retrans, ((int)$P.tp_dt_ticks) << shift);
1007		}
1008	}
1009;
1010
1011/* TP4 only */
1012SAME 			<==		TP_OPEN									TM_data_retrans
1013	($P.tp_rxtshift < TP_NRETRANS)
1014	{
1015		$P.tp_rxtshift++;
1016		(void) tp_data_retrans($P);
1017	}
1018;
1019
1020/* TP4 only */
1021SAME	 		<==		TP_CLOSING									TM_retrans
1022	(	$P.tp_retrans > 0 )
1023	{
1024		$P.tp_retrans --;
1025		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
1026		IncStat(ts_retrans_dr);
1027		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
1028	}
1029;
1030
1031/* TP4 only */
1032TP_REFWAIT 		<==		TP_CLOSING									TM_retrans
1033	DEFAULT	/* no more retrans - gave up */
1034	{
1035		$P.tp_sock->so_error = ETIMEDOUT;
1036		$P.tp_refstate = REF_FROZEN;
1037		tp_recycle_tsuffix( $P );
1038		tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
1039	}
1040;
1041
1042/*
1043 * The resources are kept around until the ref timer goes off.
1044 * The suffices are wiped out sooner so they can be reused right away.
1045 */
1046/* applicable in TP4, TP0 */
1047TP_CLOSED 		<==		TP_REFWAIT 									TM_reference
1048	DEFAULT
1049	{
1050		tp_freeref($P.tp_lref);
1051		tp_detach($P);
1052	}
1053;
1054
1055/* applicable in TP4, TP0 */
1056/* A duplicate CR from connectionless network layer can't happen */
1057SAME 			<== 	TP_OPEN 							[ CR_TPDU, CC_TPDU ]
1058	DEFAULT
1059	{
1060		if( $P.tp_class != TP_CLASS_0) {
1061			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1062			if ( $E.ev_number == CC_TPDU )
1063				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1064		}
1065		/* ignore it if class 0 - state tables are blank for this */
1066	}
1067;
1068
1069/* applicable in TP4, TP0 */
1070SAME			<== 	TP_OPEN									T_DATA_req
1071	DEFAULT
1072	{
1073		IFTRACE(D_DATA)
1074			tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb",
1075				$P.tp_sndnxt, $P.tp_snduna, $P.tp_fcredit, $P);
1076		ENDTRACE
1077
1078		tp_send($P);
1079	}
1080;
1081
1082/* TP4 only */
1083SAME			<==		TP_OPEN										T_XPD_req
1084	DEFAULT
1085		/* T_XPD_req was issued by sosend iff xpd socket buf was empty
1086		 * at time of sosend(),
1087		 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
1088		 */
1089	{
1090		int error = 0;
1091
1092		/* resume XPD */
1093		if	( $P.tp_Xsnd.sb_mb )  {
1094			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
1095			/* m_copy doesn't preserve the m_xlink field, but at this pt.
1096			 * that doesn't matter
1097			 */
1098
1099			IFTRACE(D_XPD)
1100				tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna",
1101					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
1102					$P.tp_snduna);
1103			ENDTRACE
1104			IFDEBUG(D_XPD)
1105				printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
1106				dump_mbuf(m, "XPD req emitting M");
1107			ENDDEBUG
1108			error =
1109				tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1110			$P.tp_retrans = $P.tp_Nretrans;
1111
1112			tp_ctimeout($P, TM_retrans, (int)$P.tp_rxtcur);
1113			SEQ_INC($P, $P.tp_Xsndnxt);
1114		}
1115		if(trick_hc)
1116			return error;
1117	}
1118;
1119
1120/* TP4, faked ack in TP0 when cons send completes */
1121SAME 			<==		TP_OPEN 									AK_TPDU
1122	( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq)  )
1123
1124	/* tp_goodack == true means
1125	 * EITHER it actually acked something heretofore unacknowledged
1126	 * OR no news but the credit should be processed.
1127	 */
1128	{
1129		struct sockbuf *sb = &$P.tp_sock->so_snd;
1130
1131		IFDEBUG(D_ACKRECV)
1132			printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
1133		ENDDEBUG
1134		if( $P.tp_class != TP_CLASS_0) {
1135			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1136		}
1137		sbwakeup(sb);
1138		IFDEBUG(D_ACKRECV)
1139			printf("GOOD ACK new sndnxt 0x%x\n", $P.tp_sndnxt);
1140		ENDDEBUG
1141	}
1142;
1143
1144/* TP4, and TP0 after sending a CC or possibly a CR */
1145SAME			<==		TP_OPEN 			 						 AK_TPDU
1146	DEFAULT
1147	{
1148		IFTRACE(D_ACKRECV)
1149			tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
1150				$$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
1151		ENDTRACE
1152		if( $P.tp_class != TP_CLASS_0 ) {
1153
1154			if ( !$$.e_fcc_present ) {
1155				/* send ACK with FCC */
1156				IncStat( ts_ackreason[_ACK_FCC_] );
1157				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
1158			}
1159			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1160		}
1161	}
1162;
1163
1164/* NBS(47) */
1165	/* goes in at *** */
1166		/* just so happens that this is never true now, because we allow
1167		 * only 1 packet in the queue at once (this could be changed)
1168		if	( $P.tp_Xsnd.sb_mb )  {
1169			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
1170
1171			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1172			$P.tp_retrans = $P.tp_Nretrans;
1173			tp_ctimeout($P, TM_retrans, (int)$P.tp_xpd_ticks);
1174			SEQ_INC($P, $P.tp_Xsndnxt);
1175		}
1176		 */
1177	/* end of the above hack */
1178
1179/* TP4 only */
1180SAME			<== 	TP_OPEN										XAK_TPDU
1181	( tp_goodXack($P, $$.e_seq) )
1182	/* tp_goodXack checks for good ack, removes the correct
1183	 * tpdu from the queue and  returns 1 if ack was legit, 0 if not.
1184	 * also updates tp_Xuna
1185	 */
1186	{
1187		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1188		tp_cuntimeout($P, TM_retrans);
1189
1190		sbwakeup( &$P.tp_sock->so_snd );
1191
1192		/* resume normal data */
1193		tp_send($P);
1194	}
1195;
1196
1197/* TP4, and TP0 after sending a CC or possibly a CR */
1198SAME			<==		TP_OPEN 			 						XAK_TPDU
1199	DEFAULT
1200	{
1201		IFTRACE(D_ACKRECV)
1202			tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
1203		ENDTRACE
1204		if( $P.tp_class != TP_CLASS_0 ) {
1205			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1206		}
1207	}
1208;
1209
1210/* TP4 only */
1211SAME			<==		TP_OPEN 								TM_sendack
1212	DEFAULT
1213	{
1214		int timo;
1215		IFTRACE(D_TIMER)
1216			tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
1217			$P.tp_sent_lcdt, 0);
1218		ENDTRACE
1219		IncPStat($P, tps_n_TMsendack);
1220		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1221		if ($P.tp_fcredit == 0) {
1222			if ($P.tp_rxtshift < TP_MAXRXTSHIFT)
1223				$P.tp_rxtshift++;
1224			timo = ($P.tp_dt_ticks) << $P.tp_rxtshift;
1225		} else
1226			timo = $P.tp_sendack_ticks;
1227		tp_ctimeout($P, TM_sendack, timo);
1228	}
1229;
1230
1231/* TP0 only */
1232SAME			<==		TP_OPEN 									T_USR_rcvd
1233	($P.tp_class == TP_CLASS_0)
1234	{
1235		if (sbspace(&$P.tp_sock->so_rcv) > 0)
1236			tp0_openflow($P);
1237	}
1238;
1239
1240/* TP4 only */
1241		/* If old credit was zero,
1242		 * we'd better inform other side that we now have space
1243		 * But this is not enough.  Sender might not yet have
1244		 * seen an ack with cdt 0 but it might still think the
1245		 * window is closed, so it's going to wait.
1246		 * Best to send an ack each time.
1247		 * Strictly speaking, this ought to be a function of the
1248		 * general ack strategy.
1249		 */
1250SAME			<==		TP_OPEN 									T_USR_rcvd
1251	DEFAULT
1252	{
1253		if( trick_hc ) {
1254			SeqNum ack_thresh;
1255			/*
1256			 * If the upper window edge has advanced a reasonable
1257			 * amount beyond what was known, send an ACK.
1258			 * A reasonable amount is 2 packets, unless the max window
1259			 * is only 1 or 2 packets, in which case we
1260			 * should send an ack for any advance in the upper window edge.
1261			 */
1262			LOCAL_CREDIT($P);
1263			ack_thresh = SEQ_SUB($P, $P.tp_lcredit + $P.tp_rcvnxt,
1264									 ($P.tp_maxlcredit > 2 ? 2 : 1));
1265			if (SEQ_GT($P, ack_thresh, $P.tp_sent_uwe)) {
1266				IncStat(ts_ackreason[_ACK_USRRCV_]);
1267				$P.tp_flags &= ~TPF_DELACK;
1268				return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1269			}
1270		}
1271	}
1272;
1273
1274/* applicable in TP4, TP0 */
1275SAME			<==		TP_REFWAIT 				[ T_USR_rcvd, T_USR_Xrcvd ]
1276	DEFAULT
1277	/* This happens if other end sent a DR when  the user was waiting
1278	 * on a receive.
1279	 * Processing the DR includes putting us in REFWAIT state.
1280	 */
1281	{
1282		if(trick_hc)
1283		return ECONNABORTED;
1284	}
1285;
1286
1287/* TP0 only */
1288TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT, TP_LISTENING ] 	T_NETRESET
1289	( $P.tp_class != TP_CLASS_4 )
1290		/* 0 or (4 and 0) */
1291		/* in OPEN class will be 0 or 4 but not both */
1292		/* in CRSENT or LISTENING it could be in negotiation, hence both */
1293		/* Actually, this shouldn't ever happen in LISTENING */
1294	{
1295		ASSERT( $P.tp_state != TP_LISTENING );
1296		tp_indicate(T_DISCONNECT, $P, ECONNRESET);
1297		tp_soisdisconnected($P);
1298	}
1299;
1300
1301/* TP4: ignore resets */
1302SAME		<==		[ TP_OPEN, TP_CRSENT, TP_AKWAIT,
1303						TP_CLOSING, TP_LISTENING ] 				T_NETRESET
1304	DEFAULT
1305	NULLACTION
1306;
1307
1308/* applicable in TP4, TP0 */
1309SAME			<==		[ TP_CLOSED, TP_REFWAIT ]				T_NETRESET
1310	DEFAULT
1311	NULLACTION
1312;
1313
1314/* C'EST TOUT */
1315