move tp0_stash here from tp_subr.c since we include x.25 header files
[unix-history] / usr / src / sys / netiso / tp.trans
CommitLineData
a5dd594e
KS
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{
facaece3 53/* @(#)tp.trans 7.5 (Berkeley) %G% */
a5dd594e
KS
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
a50e2bc0
KS
72#define sbwakeup(sb) sowakeup(p->tp_sock, sb);
73#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0)
a5dd594e
KS
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 */
44f52ea5 108TP_CONFIRMING /* Local to this implementation */
a5dd594e
KS
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
44f52ea5 194 T_ACPT_req
a5dd594e
KS
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
44f52ea5
KS
295TP_CONFIRMING <== TP_LISTENING CR_TPDU
296 ( $P.tp_class == TP_CLASS_0)
a5dd594e 297 {
a5dd594e 298 $P.tp_refp->tpr_state = REF_OPEN; /* has timers ??? */
a5dd594e
KS
299 }
300;
301
44f52ea5
KS
302TP_CONFIRMING <== TP_LISTENING CR_TPDU
303 DEFAULT
a5dd594e 304 {
a5dd594e
KS
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 */
44f52ea5 312 $P.tp_fcredit = $$.e_cdt;
a5dd594e
KS
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);
a50e2bc0 318 /*$P.tp_flags |= TPF_CONN_DATA_IN;*/
a5dd594e
KS
319 $$.e_data = MNULL;
320 }
44f52ea5
KS
321 }
322;
323
324TP_OPEN <== TP_CONFIRMING T_ACPT_req
325 ( $P.tp_class == TP_CLASS_0 )
326 {
327 IncStat(ts_tp0_conn);
328 IFTRACE(D_CONN)
329 tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
330 ENDTRACE
331 IFDEBUG(D_CONN)
332 printf("Confirming connection: $P" );
333 ENDDEBUG
334 soisconnected($P.tp_sock);
335 (void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ;
336 $P.tp_fcredit = 1;
337 }
338;
339
340TP_AKWAIT <== TP_CONFIRMING T_ACPT_req
341 (tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0)
342 {
343 IncStat(ts_tp4_conn); /* even though not quite open */
344 IFTRACE(D_CONN)
345 tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
346 ENDTRACE
347 IFDEBUG(D_CONN)
348 printf("Confirming connection: $P" );
349 ENDDEBUG
350 soisconnecting($P.tp_sock);
a5dd594e 351 if($P.tp_rx_strat & TPRX_FASTSTART)
44f52ea5 352 $P.tp_cong_win = $P.tp_fcredit;
a5dd594e
KS
353 $P.tp_retrans = $P.tp_Nretrans;
354 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
44f52ea5 355 }
a5dd594e
KS
356;
357
358/* TP4 only */
44f52ea5 359TP_CLOSED <== TP_CONFIRMING T_ACPT_req
a5dd594e
KS
360 DEFAULT /* emit failed */
361 {
362 register struct tp_ref *r = $P.tp_refp;
363
364 IFDEBUG(D_CONN)
365 printf("event: CR_TPDU emit CC failed done " );
366 ENDDEBUG
44f52ea5 367 soisdisconnected($P.tp_sock);
a5dd594e
KS
368 tp_recycle_tsuffix( $P );
369 tp_freeref(r);
370 tp_detach($P);
371 }
372;
373
374/* applicable in TP4, TP0 */
375TP_CRSENT <== TP_CLOSED T_CONN_req
376 DEFAULT
377 {
378 int error;
a5dd594e
KS
379 struct mbuf *data = MNULL;
380
381 IFTRACE(D_CONN)
a50e2bc0
KS
382 tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
383 $P.tp_ucddata, 0, 0);
a5dd594e 384 ENDTRACE
a50e2bc0
KS
385 data = MCPY($P.tp_ucddata, M_WAIT);
386 if (data) {
a5dd594e
KS
387 IFDEBUG(D_CONN)
388 printf("T_CONN_req.trans m_copy cc 0x%x\n",
a50e2bc0
KS
389 $P.tp_ucddata);
390 dump_mbuf(data, "sosnd @ T_CONN_req");
a5dd594e 391 ENDDEBUG
a5dd594e
KS
392 }
393
394 if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
395 return error; /* driver WON'T change state; will return error */
396
397 $P.tp_refp->tpr_state = REF_OPEN; /* has timers */
398 if($P.tp_class != TP_CLASS_0) {
399 $P.tp_retrans = $P.tp_Nretrans;
400 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
401 }
402 }
403;
404
405/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
406TP_REFWAIT <== [ TP_CRSENT, TP_AKWAIT, TP_OPEN ] DR_TPDU
407 DEFAULT
408 {
409 if ($$.e_datalen > 0 && $P.tp_class != TP_CLASS_0) {
a50e2bc0
KS
410 /*sbdrop(&$P.tp_Xrcv, $P.tp_Xrcv.sb_cc); /* purge expedited data */
411 sbflush(&$P.tp_Xrcv);
a5dd594e
KS
412 $P.tp_flags |= TPF_DISC_DATA_IN;
413 sbappendrecord(&$P.tp_Xrcv, $$.e_data);
414 $$.e_data = MNULL;
415 }
416 tp_indicate(T_DISCONNECT, $P, TP_ERROR_MASK | (u_short)$$.e_reason);
417 tp_soisdisconnected($P);
418 if ($P.tp_class != TP_CLASS_0) {
419 if ($P.tp_state == TP_OPEN ) {
420 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
421 tp_cuntimeout($P.tp_refp, TM_retrans);
422 tp_cuntimeout($P.tp_refp, TM_inact);
423 tp_cuntimeout($P.tp_refp, TM_sendack);
424 }
425 tp_cuntimeout($P.tp_refp, TM_retrans);
426 if( $$.e_sref != 0 )
427 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
428 }
429 }
430;
431
432SAME <== TP_CLOSED DR_TPDU
433 DEFAULT
434 {
435 if( $$.e_sref != 0 )
436 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
437 /* reference timer already set - reset it to be safe (???) */
438 tp_euntimeout($P.tp_refp, TM_reference); /* all */
439 tp_etimeout($P.tp_refp, TM_reference, 0, 0, 0, (int)$P.tp_refer_ticks);
440 }
441;
442
443/* NBS(34) */
444TP_REFWAIT <== TP_CRSENT ER_TPDU
445 DEFAULT
446 {
447 tp_cuntimeout($P.tp_refp, TM_retrans);
448 tp_indicate(T_DISCONNECT, $P,
449 TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
450 tp_soisdisconnected($P);
451 }
452;
453
454/* NBS(27) */
455TP_REFWAIT <== TP_CLOSING DR_TPDU
456 DEFAULT
457 {
458 $P.tp_sock->so_error = (u_short)$$.e_reason;
459 tp_cuntimeout($P.tp_refp, TM_retrans);
460 tp_soisdisconnected($P);
461 }
462;
463/* these two transitions are the same but can't be combined because xebec
464 * can't handle the use of $$.e_reason if they're combined
465 */
466/* NBS(27) */
467TP_REFWAIT <== TP_CLOSING ER_TPDU
468 DEFAULT
469 {
470 $P.tp_sock->so_error = (u_short)$$.e_reason;
471 tp_cuntimeout($P.tp_refp, TM_retrans);
472 tp_soisdisconnected($P);
473 }
474;
475/* NBS(27) */
476TP_REFWAIT <== TP_CLOSING DC_TPDU
477 DEFAULT
478 {
479 tp_cuntimeout($P.tp_refp, TM_retrans);
480 tp_soisdisconnected($P);
481 }
482;
483
484/* NBS(21) */
485SAME <== TP_CLOSED [ CC_TPDU, CR_TPDU ]
486 DEFAULT
487 { /* don't ask me why we have to do this - spec says so */
488 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
489 /* don't bother with retransmissions of the DR */
490 }
491;
492
493/* NBS(34) */
494TP_REFWAIT <== TP_OPEN ER_TPDU
495 ($P.tp_class == TP_CLASS_0)
496 {
497 tp_soisdisconnecting($P.tp_sock);
498 tp_indicate(T_DISCONNECT, $P,
499 TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
500
501 tp_soisdisconnected($P);
502 tp_netcmd( $P, CONN_CLOSE );
503 }
504;
505
506TP_CLOSING <== [ TP_AKWAIT, TP_OPEN ] ER_TPDU
507 DEFAULT
508 {
509 if ($P.tp_state == TP_OPEN) {
510 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
511 tp_cuntimeout($P.tp_refp, TM_inact);
512 tp_cuntimeout($P.tp_refp, TM_sendack);
513 }
514 tp_soisdisconnecting($P.tp_sock);
515 tp_indicate(T_DISCONNECT, $P,
516 TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
517 $P.tp_retrans = $P.tp_Nretrans;
518 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
519 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
520 }
521;
522/* NBS(6) */
523TP_OPEN <== TP_CRSENT CC_TPDU
524 ($P.tp_class == TP_CLASS_0)
525 {
526 tp_cuntimeout($P.tp_refp, TM_retrans);
527 IncStat(ts_tp0_conn);
528 $P.tp_fcredit = 1;
529 soisconnected($P.tp_sock);
530 }
531;
532
533TP_OPEN <== TP_CRSENT CC_TPDU
534 DEFAULT
535 {
536 IFDEBUG(D_CONN)
537 printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
538 (int)$P.tp_flags);
539 ENDDEBUG
540 IncStat(ts_tp4_conn);
541 $P.tp_fref = $$.e_sref;
542 $P.tp_fcredit = $$.e_cdt;
5cefd778 543 $P.tp_ackrcvd = 0;
a5dd594e
KS
544 if($P.tp_rx_strat & TPRX_FASTSTART)
545 $P.tp_cong_win = $$.e_cdt;
546 tp_getoptions($P);
547 tp_cuntimeout($P.tp_refp, TM_retrans);
a50e2bc0 548 if ($P.tp_ucddata) {
a5dd594e 549 IFDEBUG(D_CONN)
a50e2bc0
KS
550 printf("dropping user connect data cc 0x%x\n",
551 $P.tp_ucddata->m_len);
a5dd594e 552 ENDDEBUG
a50e2bc0
KS
553 m_freem($P.tp_ucddata);
554 $P.tp_ucddata = 0;
a5dd594e
KS
555 }
556 soisconnected($P.tp_sock);
557 if ($$.e_datalen > 0) {
558 ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
559 sbappendrecord(&$P.tp_Xrcv, $$.e_data);
560 $P.tp_flags |= TPF_CONN_DATA_IN;
561 $$.e_data = MNULL;
562 }
563
564 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
565 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
566 }
567;
568
569/* TP4 only */
570SAME <== TP_CRSENT TM_retrans
571 ( $P.tp_retrans > 0 )
572 {
a5dd594e
KS
573 struct mbuf *data = MNULL;
574 int error;
575
576 IncStat(ts_retrans_cr);
5cefd778
KS
577 $P.tp_cong_win = 1;
578 $P.tp_ackrcvd = 0;
a50e2bc0
KS
579 data = MCPY($P.tp_ucddata, M_NOWAIT);
580 if($P.tp_ucddata) {
a5dd594e 581 IFDEBUG(D_CONN)
a50e2bc0
KS
582 printf("TM_retrans.trans m_copy cc 0x%x\n", data);
583 dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
a5dd594e 584 ENDDEBUG
a5dd594e
KS
585 if( data == MNULL )
586 return ENOBUFS;
587 }
588
589 $P.tp_retrans --;
590 if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
591 $P.tp_sock->so_error = error;
592 }
593 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
594 }
595;
596
597/* TP4 only */
598TP_REFWAIT <== TP_CRSENT TM_retrans
599 DEFAULT /* no more CR retransmissions */
600 {
601 IncStat(ts_conn_gaveup);
602 $P.tp_sock->so_error = ETIMEDOUT;
603 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
604 tp_soisdisconnected($P);
605 }
606;
607
608/* TP4 only */
609SAME <== TP_AKWAIT CR_TPDU
610 DEFAULT
611 /* duplicate CR (which doesn't really exist in the context of
612 * a connectionless network layer)
613 * Doesn't occur in class 0.
614 */
615 {
616 int error;
a50e2bc0 617 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
a5dd594e 618
a50e2bc0 619 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
a5dd594e
KS
620 $P.tp_sock->so_error = error;
621 }
622 $P.tp_retrans = $P.tp_Nretrans;
623 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
624 }
625;
626
627/* TP4 only */
628TP_OPEN <== TP_AKWAIT DT_TPDU
629 ( IN_RWINDOW( $P, $$.e_seq,
630 $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
631 {
632 int doack;
633
a50e2bc0
KS
634 /*
635 * Get rid of any confirm or connect data, so that if we
636 * crash or close, it isn't thought of as disconnect data.
637 */
638 if ($P.tp_ucddata) {
639 m_freem($P.tp_ucddata);
640 $P.tp_ucddata = 0;
a5dd594e 641 }
a5dd594e
KS
642 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
643 tp_cuntimeout($P.tp_refp, TM_retrans);
644 soisconnected($P.tp_sock);
645 tp_getoptions($P);
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 {
a50e2bc0
KS
733 if ($P.tp_ucddata) {
734 m_freem($P.tp_ucddata);
735 $P.tp_ucddata = 0;
a5dd594e 736 }
a5dd594e
KS
737 (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
738 tp_cuntimeout($P.tp_refp, TM_retrans);
739
740 tp_getoptions($P);
741 soisconnected($P.tp_sock);
742 IFTRACE(D_CONN)
743 struct socket *so = $P.tp_sock;
744 tptrace(TPPTmisc,
745 "called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
746 so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
747 tptrace(TPPTmisc,
748 "called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
749 so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
750 ENDTRACE
751
752 tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
753 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
754 }
755;
756
757/* TP4 only */
758TP_OPEN <== [ TP_OPEN, TP_AKWAIT ] XPD_TPDU
a50e2bc0 759 ( $P.tp_Xrcvnxt == $$.e_seq /* && $P.tp_Xrcv.sb_cc == 0*/)
a5dd594e
KS
760 {
761 if( $P.tp_state == TP_AKWAIT ) {
a50e2bc0
KS
762 if ($P.tp_ucddata) {
763 m_freem($P.tp_ucddata);
764 $P.tp_ucddata = 0;
a5dd594e
KS
765 }
766 tp_cuntimeout($P.tp_refp, TM_retrans);
767 tp_getoptions($P);
768 soisconnected($P.tp_sock);
769 tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
770 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
771 }
772 IFTRACE(D_XPD)
773 tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
774 $P.tp_Xrcvnxt,$$.e_seq, $$.e_datalen, $$.e_data->m_len);
775 ENDTRACE
776
a50e2bc0
KS
777 $P.tp_sock->so_state |= SS_RCVATMARK;
778 sbinsertoob(&$P.tp_Xrcv, $$.e_data);
a5dd594e
KS
779 IFDEBUG(D_XPD)
780 dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
781 ENDDEBUG
a5dd594e
KS
782 tp_indicate(T_XDATA, $P, 0);
783 sbwakeup( &$P.tp_Xrcv );
784
785 (void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
786 SEQ_INC($P, $P.tp_Xrcvnxt);
787 }
788;
789
790/* TP4 only */
791SAME <== TP_OPEN T_USR_Xrcvd
792 DEFAULT
793 {
794 if( $P.tp_Xrcv.sb_cc == 0 ) {
a50e2bc0 795 /*$P.tp_flags &= ~TPF_XPD_PRESENT;*/
a5dd594e 796 /* kludge for select(): */
a50e2bc0 797 /* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
a5dd594e
KS
798 }
799 }
800 /* OLD WAY:
801 * Ack only after the user receives the XPD. This is better for
802 * users that use one XPD right after another.
803 * Acking right away (the NEW WAY, see the prev. transition) is
804 * better for occasional * XPD, when the receiving user doesn't
805 * want to read the XPD immediately (which is session's behavior).
806 *
807 int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
808 SEQ_INC($P, $P.tp_Xrcvnxt);
809 return error;
810 */
811;
812
813/* NOTE: presently if the user doesn't read the connection data
814 * before and expedited data PDU comes in, the connection data will
815 * be dropped. This is a bug. To avoid it, we need somewhere else
816 * to put the connection data.
817 * On the other hand, we need not to have it sitting around forever.
818 * This is a problem with the idea of trying to accommodate
819 * data on connect w/ a passive-open user interface.
820 */
821/* TP4 only */
822
823SAME <== [ TP_AKWAIT, TP_OPEN ] XPD_TPDU
824 DEFAULT /* not in window or cdt==0 */
825 {
826 IFTRACE(D_XPD)
827 tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
828 $P.tp_Xrcvnxt, $$.e_seq, $P.tp_Xrcv.sb_cc , 0);
829 ENDTRACE
830 if( $P.tp_Xrcvnxt != $$.e_seq )
831 IncStat(ts_xpd_niw);
832 if( $P.tp_Xrcv.sb_cc ) {
a50e2bc0 833#ifdef notdef
a5dd594e
KS
834 if( $P.tp_flags & TPF_CONN_DATA_IN ) {
835 /* user isn't reading the connection data; see note above */
836 sbdrop(&$P.tp_Xrcv, $P.tp_Xrcv.sb_cc);
837 $P.tp_flags &= ~TPF_CONN_DATA_IN;
838 }
a50e2bc0 839#endif notdef
a5dd594e
KS
840 /* might as well kick 'em again */
841 tp_indicate(T_XDATA, $P, 0);
842 IncStat(ts_xpd_dup);
843 }
844 m_freem($$.e_data);
845 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
846 /* don't send an xack because the xak gives "last one received", not
847 * "next one i expect" (dumb)
848 */
849 }
850;
851
852/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
853 * to detach all its "children"
854 * Also (CRSENT) when user kills a job that's doing a connect()
855 */
856TP_REFWAIT <== TP_CRSENT T_DETACH
857 ($P.tp_class == TP_CLASS_0)
858 {
859 struct socket *so = $P.tp_sock;
860
861 /* detach from parent socket so it can finish closing */
862 if (so->so_head) {
863 if (!soqremque(so, 0) && !soqremque(so, 1))
864 panic("tp: T_DETACH");
865 so->so_head = 0;
866 }
867 tp_soisdisconnecting($P.tp_sock);
868 tp_netcmd( $P, CONN_CLOSE);
869 tp_soisdisconnected($P);
870 }
871;
872
873/* TP4 only */
44f52ea5 874TP_CLOSING <== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ] T_DETACH
a5dd594e
KS
875 DEFAULT
876 {
877 struct socket *so = $P.tp_sock;
a50e2bc0 878 struct mbuf *data = MNULL;
a5dd594e
KS
879
880 /* detach from parent socket so it can finish closing */
881 if (so->so_head) {
882 if (!soqremque(so, 0) && !soqremque(so, 1))
883 panic("tp: T_DETACH");
884 so->so_head = 0;
885 }
886 if ($P.tp_state != TP_CLOSING) {
887 tp_soisdisconnecting($P.tp_sock);
a50e2bc0
KS
888 data = MCPY($P.tp_ucddata, M_NOWAIT);
889 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
a5dd594e
KS
890 $P.tp_retrans = $P.tp_Nretrans;
891 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
892 }
893 }
894;
895
896TP_REFWAIT <== [ TP_OPEN, TP_CRSENT ] T_DISC_req
897 ( $P.tp_class == TP_CLASS_0 )
898 {
899 tp_soisdisconnecting($P.tp_sock);
900 tp_netcmd( $P, CONN_CLOSE);
901 tp_soisdisconnected($P);
902 }
903;
904
905/* TP4 only */
44f52ea5 906TP_CLOSING <== [ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ] T_DISC_req
a5dd594e
KS
907 DEFAULT
908 {
a50e2bc0 909 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
a5dd594e
KS
910
911 if($P.tp_state == TP_OPEN) {
912 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
913 tp_cuntimeout($P.tp_refp, TM_inact);
914 tp_cuntimeout($P.tp_refp, TM_sendack);
915 }
a50e2bc0 916 if (data) {
a5dd594e 917 IFDEBUG(D_CONN)
a50e2bc0
KS
918 printf("T_DISC_req.trans tp_ucddata 0x%x\n",
919 $P.tp_ucddata);
920 dump_mbuf(data, "ucddata @ T_DISC_req");
a5dd594e 921 ENDDEBUG
a5dd594e 922 }
a5dd594e
KS
923 tp_soisdisconnecting($P.tp_sock);
924 $P.tp_retrans = $P.tp_Nretrans;
925 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
926
927 if( trick_hc )
928 return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
929 }
930;
931
932/* TP4 only */
933SAME <== TP_AKWAIT TM_retrans
934 ( $P.tp_retrans > 0 )
935 {
936 int error;
a50e2bc0 937 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
a5dd594e
KS
938
939 IncStat(ts_retrans_cc);
940 $P.tp_retrans --;
5cefd778
KS
941 $P.tp_cong_win = 1;
942 $P.tp_ackrcvd = 0;
943
a50e2bc0 944 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
a5dd594e
KS
945 $P.tp_sock->so_error = error;
946 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
947 }
948;
949
950/* TP4 only */
951TP_CLOSING <== TP_AKWAIT TM_retrans
952 DEFAULT /* out of time */
953 {
954 IncStat(ts_conn_gaveup);
955 tp_soisdisconnecting($P.tp_sock);
956 $P.tp_sock->so_error = ETIMEDOUT;
957 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
958 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
959 $P.tp_retrans = $P.tp_Nretrans;
960 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
961 }
962;
963
964/* the retrans timers had better go off BEFORE the inactivity timer does,
965 * if transmissions are going on.
966 * (i.e., TM_inact should be greater than timer for all retrans plus ack
967 * turnaround)
968 */
969/* TP4 only */
970TP_CLOSING <== TP_OPEN [ TM_inact, TM_retrans, TM_data_retrans ]
971 DEFAULT
972 {
973 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
974 tp_cuntimeout($P.tp_refp, TM_inact);
975 tp_cuntimeout($P.tp_refp, TM_sendack);
976
977 IncStat(ts_conn_gaveup);
978 tp_soisdisconnecting($P.tp_sock);
979 $P.tp_sock->so_error = ETIMEDOUT;
980 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
981 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
982 $P.tp_retrans = $P.tp_Nretrans;
983 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
984 }
985;
986
987/* TP4 only */
988SAME <== TP_OPEN TM_retrans
989 ( $P.tp_retrans > 0 )
990 {
5cefd778
KS
991 $P.tp_cong_win = 1;
992 $P.tp_ackrcvd = 0;
a5dd594e
KS
993 /* resume XPD */
994 if ( $P.tp_Xsnd.sb_mb ) {
a50e2bc0 995 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
a5dd594e
KS
996 /* m_copy doesn't preserve the m_xlink field, but at this pt.
997 * that doesn't matter
998 */
999
1000 IFTRACE(D_XPD)
1001 tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndhiwat snduna",
1002 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
1003 $P.tp_snduna);
1004 ENDTRACE
1005 IFDEBUG(D_XPD)
1006 dump_mbuf(m, "XPD retrans emitting M");
1007 ENDDEBUG
1008 IncStat(ts_retrans_xpd);
1009 $P.tp_retrans --;
1010 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1011 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1012 }
1013 }
1014;
1015
1016/* TP4 only */
1017SAME <== TP_OPEN TM_data_retrans
1018 ( $$.e_retrans > 0 )
1019 {
1020 register SeqNum low, lowsave = 0;
1021 register struct tp_rtc *r = $P.tp_snduna_rtc;
1022 register struct mbuf *m;
1023 register SeqNum high = $$.e_high;
a5dd594e 1024
5cefd778
KS
1025 low = $P.tp_snduna;
1026 lowsave = high = low;
1027
1028 tp_euntimeout_lss($P.tp_refp, TM_data_retrans,
1029 SEQ_ADD($P, $P.tp_sndhiwat, 1));
1030 $P.tp_retrans_hiwat = $P.tp_sndhiwat;
1031
a5dd594e
KS
1032 if (($P.tp_rx_strat & TPRX_EACH) == 0)
1033 high = (high>low)?low:high;
1034
1035 if( $P.tp_rx_strat & TPRX_USE_CW ) {
1036 register int i;
1037
1038 $P.tp_cong_win = 1;
5cefd778 1039 $P.tp_ackrcvd = 0;
a5dd594e 1040 i = SEQ_ADD($P, low, $P.tp_cong_win);
5cefd778
KS
1041
1042 high = SEQ_MIN($P, high, $P.tp_sndhiwat);
1043
a5dd594e
KS
1044 }
1045
1046 while( SEQ_LEQ($P, low, high) ){
1047 if ( r == (struct tp_rtc *)0 ){
1048 IFDEBUG(D_RTC)
1049 printf( "tp: retrans rtc list is GONE!\n");
1050 ENDDEBUG
1051 break;
1052 }
1053 if ( r->tprt_seq == low ){
1054 if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))== MNULL)
1055 break;
1056 (void) tp_emit(DT_TPDU_type, $P, low, r->tprt_eot, m);
1057 IncStat(ts_retrans_dt);
1058 SEQ_INC($P, low );
1059 }
1060 r = r->tprt_next;
1061 }
5cefd778 1062/* CE_BIT
a5dd594e 1063 if ( SEQ_LEQ($P, lowsave, high) ){
5cefd778 1064*/
a5dd594e
KS
1065 $$.e_retrans --;
1066 tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)lowsave,
1067 (caddr_t)high, $$.e_retrans,
1068 ($P.tp_Nretrans - $$.e_retrans) * (int)$P.tp_dt_ticks);
5cefd778 1069/* CE_BIT
a5dd594e 1070 }
5cefd778 1071*/
a5dd594e
KS
1072 }
1073;
1074
1075/* TP4 only */
1076SAME <== TP_CLOSING TM_retrans
1077 ( $P.tp_retrans > 0 )
1078 {
1079 $P.tp_retrans --;
1080 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
1081 IncStat(ts_retrans_dr);
1082 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
1083 }
1084;
1085
1086/* TP4 only */
1087TP_REFWAIT <== TP_CLOSING TM_retrans
1088 DEFAULT /* no more retrans - gave up */
1089 {
1090 $P.tp_sock->so_error = ETIMEDOUT;
1091 $P.tp_refp->tpr_state = REF_FROZEN;
1092 tp_recycle_tsuffix( $P );
1093 tp_etimeout($P.tp_refp, TM_reference, 0,0,0, (int)$P.tp_refer_ticks);
1094 }
1095;
1096
1097/*
1098 * The resources are kept around until the ref timer goes off.
1099 * The suffices are wiped out sooner so they can be reused right away.
1100 */
1101/* applicable in TP4, TP0 */
1102TP_CLOSED <== TP_REFWAIT TM_reference
1103 DEFAULT
1104 {
1105 tp_freeref($P.tp_refp);
1106 tp_detach($P);
1107 }
1108;
1109
1110/* applicable in TP4, TP0 */
1111/* A duplicate CR from connectionless network layer can't happen */
1112SAME <== TP_OPEN [ CR_TPDU, CC_TPDU ]
1113 DEFAULT
1114 {
1115 if( $P.tp_class != TP_CLASS_0) {
1116 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1117 if ( $E.ev_number == CC_TPDU )
1118 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1119 }
1120 /* ignore it if class 0 - state tables are blank for this */
1121 }
1122;
1123
1124/* applicable in TP4, TP0 */
1125SAME <== TP_OPEN T_DATA_req
1126 DEFAULT
1127 {
1128 IFTRACE(D_DATA)
1129 tptrace(TPPTmisc, "T_DATA_req sndhiwat snduna fcredit, tpcb",
1130 $P.tp_sndhiwat, $P.tp_snduna, $P.tp_fcredit, $P);
1131 ENDTRACE
1132
1133 tp_send($P);
1134 }
1135;
1136
1137/* TP4 only */
1138SAME <== TP_OPEN T_XPD_req
1139 DEFAULT
1140 /* T_XPD_req was issued by sosend iff xpd socket buf was empty
1141 * at time of sosend(),
1142 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
1143 */
1144 {
1145 int error = 0;
1146
1147 /* resume XPD */
1148 if ( $P.tp_Xsnd.sb_mb ) {
a50e2bc0 1149 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
a5dd594e
KS
1150 /* m_copy doesn't preserve the m_xlink field, but at this pt.
1151 * that doesn't matter
1152 */
1153
1154 IFTRACE(D_XPD)
1155 tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndhiwat snduna",
1156 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
1157 $P.tp_snduna);
1158 ENDTRACE
1159 IFDEBUG(D_XPD)
1160 printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
1161 dump_mbuf(m, "XPD req emitting M");
1162 ENDDEBUG
1163 error =
1164 tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1165 $P.tp_retrans = $P.tp_Nretrans;
1166 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1167 SEQ_INC($P, $P.tp_Xsndnxt);
1168 }
1169 if(trick_hc)
1170 return error;
1171 }
1172;
1173
1174/* TP4, faked ack in TP0 when cons send completes */
1175SAME <== TP_OPEN AK_TPDU
1176 ( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq) )
1177
1178 /* tp_goodack == true means
1179 * EITHER it actually acked something heretofore unacknowledged
1180 * OR no news but the credit should be processed.
1181 */
1182 {
1183 IFDEBUG(D_ACKRECV)
1184 printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
1185 ENDDEBUG
1186 if( $P.tp_class != TP_CLASS_0) {
1187 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1188 tp_euntimeout_lss($P.tp_refp, TM_data_retrans, $$.e_seq);
1189 }
1190 sbwakeup( &$P.tp_sock->so_snd );
1191
5cefd778
KS
1192 if ($P.tp_sndhiwat <= $P.tp_retrans_hiwat &&
1193 $P.tp_snduna <= $P.tp_retrans_hiwat) {
1194
1195 register struct mbuf *m;
1196 /* extern struct mbuf *m_copy(); */
1197 register struct tp_rtc *r;
1198 SeqNum high, retrans, low_save;
1199
1200 high = SEQ_MIN($P, SEQ_ADD($P, $P.tp_snduna,
1201 MIN($P.tp_cong_win, $P.tp_fcredit)) - 1,
1202 $P.tp_sndhiwat);
1203 low_save = retrans = SEQ_MAX($P, SEQ_ADD($P, $P.tp_last_retrans, 1),
1204 $P.tp_snduna);
1205 for (; SEQ_LEQ($P, retrans, high); SEQ_INC($P, retrans)) {
1206
1207 for (r = $P.tp_snduna_rtc; r; r = r->tprt_next){
1208 if ( r->tprt_seq == retrans ){
1209 if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))
1210 == MNULL)
1211 break;
1212 (void) tp_emit(DT_TPDU_type, $P, retrans,
1213 r->tprt_eot, m);
1214 $P.tp_last_retrans = retrans;
1215 IncStat(ts_retrans_dt);
1216 break;
1217 }
1218 }
1219 if ( r == (struct tp_rtc *)0 ){
1220 IFDEBUG(D_RTC)
1221 printf( "tp: retrans rtc list is GONE!\n");
1222 ENDDEBUG
1223 break;
1224 }
1225 }
1226 tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)low_save,
1227 (caddr_t)high, $P.tp_retrans, (int)$P.tp_dt_ticks);
1228 if (SEQ_DEC($P, retrans) == $P.tp_retrans_hiwat)
1229 tp_send($P);
1230 }
1231 else {
1232 tp_send($P);
1233 }
a5dd594e
KS
1234 IFDEBUG(D_ACKRECV)
1235 printf("GOOD ACK new sndhiwat 0x%x\n", $P.tp_sndhiwat);
1236 ENDDEBUG
1237 }
1238;
1239
a5dd594e
KS
1240/* TP4, and TP0 after sending a CC or possibly a CR */
1241SAME <== TP_OPEN AK_TPDU
1242 DEFAULT
1243 {
1244 IFTRACE(D_ACKRECV)
1245 tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
1246 $$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
1247 ENDTRACE
1248 if( $P.tp_class != TP_CLASS_0 ) {
1249
1250 if ( !$$.e_fcc_present ) {
1251 /* send ACK with FCC */
1252 IncStat( ts_ackreason[_ACK_FCC_] );
1253 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
1254 }
1255 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1256 }
1257 }
1258;
1259
1260/* NBS(47) */
1261 /* goes in at *** */
1262 /* just so happens that this is never true now, because we allow
1263 * only 1 packet in the queue at once (this could be changed)
1264 if ( $P.tp_Xsnd.sb_mb ) {
a5dd594e
KS
1265 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
1266
1267 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1268 $P.tp_retrans = $P.tp_Nretrans;
1269 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1270 SEQ_INC($P, $P.tp_Xsndnxt);
1271 }
1272 */
1273 /* end of the above hack */
1274
1275/* TP4 only */
facaece3 1276SAME <== TP_OPEN XAK_TPDU
a5dd594e
KS
1277 ( tp_goodXack($P, $$.e_seq) )
1278 /* tp_goodXack checks for good ack, removes the correct
1279 * tpdu from the queue and returns 1 if ack was legit, 0 if not.
1280 * also updates tp_Xuna
1281 */
1282 {
1283 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1284 tp_cuntimeout($P.tp_refp, TM_retrans);
1285
1286 sbwakeup( &$P.tp_sock->so_snd );
1287
1288 /* resume normal data */
1289 tp_send($P);
1290 }
1291;
1292
facaece3
KS
1293/* TP4, and TP0 after sending a CC or possibly a CR */
1294SAME <== TP_OPEN XAK_TPDU
1295 DEFAULT
1296 {
1297 IFTRACE(D_ACKRECV)
1298 tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
1299 ENDTRACE
1300 if( $P.tp_class != TP_CLASS_0 ) {
1301 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1302 }
1303 }
1304;
1305
a5dd594e
KS
1306/* TP4 only */
1307SAME <== TP_OPEN TM_sendack
1308 DEFAULT
1309 {
1310 IFTRACE(D_TIMER)
1311 tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
1312 $P.tp_sent_lcdt, 0);
1313 ENDTRACE
1314 IncPStat($P, tps_n_TMsendack);
1315 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1316 }
1317;
1318
1319/* TP0 only */
1320SAME <== TP_OPEN T_USR_rcvd
1321 ($P.tp_class == TP_CLASS_0)
1322 NULLACTION
1323;
1324
1325/* TP4 only */
1326 /* If old credit was zero,
1327 * we'd better inform other side that we now have space
1328 * But this is not enough. Sender might not yet have
1329 * seen an ack with cdt 0 but it might still think the
1330 * window is closed, so it's going to wait.
1331 * Best to send an ack each time.
1332 * Strictly speaking, this ought to be a function of the
1333 * general ack strategy.
1334 */
1335SAME <== TP_OPEN T_USR_rcvd
1336 DEFAULT
1337 {
1338 if( trick_hc ) {
1339 IncStat(ts_ackreason[_ACK_USRRCV_]);
5cefd778
KS
1340
1341 /* send an ACK only if there's new information */
1342 LOCAL_CREDIT( $P );
1343 if (($P.tp_rcvnxt != $P.tp_sent_rcvnxt) ||
1344 ($P.tp_lcredit != $P.tp_sent_lcdt))
1345
1346 return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
a5dd594e
KS
1347 }
1348 }
1349;
1350
1351/* applicable in TP4, TP0 */
1352SAME <== TP_REFWAIT [ T_USR_rcvd, T_USR_Xrcvd ]
1353 DEFAULT
1354 /* This happens if other end sent a DR when the user was waiting
1355 * on a receive.
1356 * Processing the DR includes putting us in REFWAIT state.
1357 */
1358 {
1359 if(trick_hc)
1360 return ECONNABORTED;
1361 }
1362;
1363
1364/* TP0 only */
1365TP_REFWAIT <== [ TP_OPEN, TP_CRSENT, TP_LISTENING ] T_NETRESET
1366 ( $P.tp_class != TP_CLASS_4 )
1367 /* 0 or (4 and 0) */
1368 /* in OPEN class will be 0 or 4 but not both */
1369 /* in CRSENT or LISTENING it could be in negotiation, hence both */
1370 /* Actually, this shouldn't ever happen in LISTENING */
1371 {
1372 ASSERT( $P.tp_state != TP_LISTENING );
1373 tp_indicate(T_DISCONNECT, $P, ECONNRESET);
1374 tp_soisdisconnected($P);
1375 }
1376;
1377
1378/* TP4: ignore resets */
1379SAME <== [ TP_OPEN, TP_CRSENT, TP_AKWAIT,
1380 TP_CLOSING, TP_LISTENING ] T_NETRESET
1381 DEFAULT
1382 NULLACTION
1383;
1384
1385/* applicable in TP4, TP0 */
1386SAME <== [ TP_CLOSED, TP_REFWAIT ] T_NETRESET
1387 DEFAULT
1388 NULLACTION
1389;
1390
1391/* C'EST TOUT */