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