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