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