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