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