ax25sdl 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +59 -0
  2. package/dist/ax25sdl/awaiting_connection.g.d.ts +7 -0
  3. package/dist/ax25sdl/awaiting_connection.g.d.ts.map +1 -0
  4. package/dist/ax25sdl/awaiting_connection.g.js +427 -0
  5. package/dist/ax25sdl/awaiting_connection.g.js.map +1 -0
  6. package/dist/ax25sdl/awaiting_connection.g.test.d.ts +2 -0
  7. package/dist/ax25sdl/awaiting_connection.g.test.d.ts.map +1 -0
  8. package/dist/ax25sdl/awaiting_connection.g.test.js +345 -0
  9. package/dist/ax25sdl/awaiting_connection.g.test.js.map +1 -0
  10. package/dist/ax25sdl/awaiting_release.g.d.ts +7 -0
  11. package/dist/ax25sdl/awaiting_release.g.d.ts.map +1 -0
  12. package/dist/ax25sdl/awaiting_release.g.js +344 -0
  13. package/dist/ax25sdl/awaiting_release.g.js.map +1 -0
  14. package/dist/ax25sdl/awaiting_release.g.test.d.ts +2 -0
  15. package/dist/ax25sdl/awaiting_release.g.test.d.ts.map +1 -0
  16. package/dist/ax25sdl/awaiting_release.g.test.js +255 -0
  17. package/dist/ax25sdl/awaiting_release.g.test.js.map +1 -0
  18. package/dist/ax25sdl/awaiting_v22_connection.g.d.ts +7 -0
  19. package/dist/ax25sdl/awaiting_v22_connection.g.d.ts.map +1 -0
  20. package/dist/ax25sdl/awaiting_v22_connection.g.js +428 -0
  21. package/dist/ax25sdl/awaiting_v22_connection.g.js.map +1 -0
  22. package/dist/ax25sdl/awaiting_v22_connection.g.test.d.ts +2 -0
  23. package/dist/ax25sdl/awaiting_v22_connection.g.test.d.ts.map +1 -0
  24. package/dist/ax25sdl/awaiting_v22_connection.g.test.js +372 -0
  25. package/dist/ax25sdl/awaiting_v22_connection.g.test.js.map +1 -0
  26. package/dist/ax25sdl/connected.g.d.ts +7 -0
  27. package/dist/ax25sdl/connected.g.d.ts.map +1 -0
  28. package/dist/ax25sdl/connected.g.js +1456 -0
  29. package/dist/ax25sdl/connected.g.js.map +1 -0
  30. package/dist/ax25sdl/connected.g.test.d.ts +2 -0
  31. package/dist/ax25sdl/connected.g.test.d.ts.map +1 -0
  32. package/dist/ax25sdl/connected.g.test.js +1313 -0
  33. package/dist/ax25sdl/connected.g.test.js.map +1 -0
  34. package/dist/ax25sdl/disconnected.g.d.ts +7 -0
  35. package/dist/ax25sdl/disconnected.g.d.ts.map +1 -0
  36. package/dist/ax25sdl/disconnected.g.js +340 -0
  37. package/dist/ax25sdl/disconnected.g.js.map +1 -0
  38. package/dist/ax25sdl/disconnected.g.test.d.ts +2 -0
  39. package/dist/ax25sdl/disconnected.g.test.d.ts.map +1 -0
  40. package/dist/ax25sdl/disconnected.g.test.js +266 -0
  41. package/dist/ax25sdl/disconnected.g.test.js.map +1 -0
  42. package/dist/ax25sdl/index.d.ts +8 -0
  43. package/dist/ax25sdl/index.d.ts.map +1 -0
  44. package/dist/ax25sdl/index.js +10 -0
  45. package/dist/ax25sdl/index.js.map +1 -0
  46. package/dist/ax25sdl/sdl.test.d.ts +2 -0
  47. package/dist/ax25sdl/sdl.test.d.ts.map +1 -0
  48. package/dist/ax25sdl/sdl.test.js +47 -0
  49. package/dist/ax25sdl/sdl.test.js.map +1 -0
  50. package/dist/ax25sdl/subroutines.g.d.ts +7 -0
  51. package/dist/ax25sdl/subroutines.g.d.ts.map +1 -0
  52. package/dist/ax25sdl/subroutines.g.js +497 -0
  53. package/dist/ax25sdl/subroutines.g.js.map +1 -0
  54. package/dist/ax25sdl/types.d.ts +94 -0
  55. package/dist/ax25sdl/types.d.ts.map +1 -0
  56. package/dist/ax25sdl/types.js +10 -0
  57. package/dist/ax25sdl/types.js.map +1 -0
  58. package/package.json +46 -0
@@ -0,0 +1,1456 @@
1
+ // Code generated by tools/Packet.Sdl.CodeGen from spec-sdl/data-link/connected.sdl.yaml.
2
+ // DO NOT EDIT. Run `dotnet run --project tools/Packet.Sdl.CodeGen` to regenerate.
3
+ /**
4
+ * SDL transitions for the Connected state of the data_link machine.
5
+ * Source: ax.25.2.2.4_Oct_25, figure figc4.4.
6
+ */
7
+ export const DataLinkConnected = {
8
+ machine: "data_link",
9
+ state: "Connected",
10
+ source: { spec: "ax.25.2.2.4_Oct_25", figure: "figc4.4", url: "https://raw.githubusercontent.com/packethacking/ax25spec/main/doc/media/figc4.4a.png" },
11
+ transitions: [
12
+ {
13
+ id: "t01_dl_disconnect_request",
14
+ from: "Connected",
15
+ on: "DL_DISCONNECT_request",
16
+ guard: "",
17
+ actions: [
18
+ { verb: "discard_I_frame_queue", kind: "processing" },
19
+ { verb: "RC := 0", kind: "processing" },
20
+ { verb: "DISC (P = 1)", kind: "signal_lower" },
21
+ { verb: "stop_T3", kind: "processing" },
22
+ { verb: "start_T1", kind: "processing" },
23
+ ],
24
+ next: "AwaitingRelease",
25
+ notes: "Upper layer requests disconnect. Tear down: discard pending I\nframes, reset retry counter, send DISC with P=1, swap timers\n(stop T3, start T1 for the DISC ack wait), → AwaitingRelease.\n",
26
+ references: [
27
+ { source: "spec_prose", cite: "§6.3.4 ¶1", quote: "While in the information-transfer state, either TNC may indicate a request to disconnect the link by transmitting a DISC command frame and starting timer T1.", path: "", function: "", line: 0, note: "" },
28
+ { source: "linbpq", cite: "", quote: "", path: "L4Code.c", function: "CLOSECURRENTSESSION", line: 1086, note: "sets DISCPENDING if TX_Q non-empty; immediate state 4 + T1=1 otherwise — figure says always discard + DISC(P=1)" },
29
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "dl_disconnect_request", line: 1170, note: "state_3/state_4 fall-through: discard queue, DISC cmd P=1, STOP_T3 / START_T1, → state_2" },
30
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::disconnect", line: 1472, note: "fused Connected+TimerRecovery; clears iframe queue, rc=0, sends DISC, → AwaitingRelease. Author TODO flag on timer choice." },
31
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/af_ax25.c", function: "ax25_release", line: 1031, note: "STATE_3/STATE_4 branch — Linux collapses Connected and TimerRecovery into one DISC path" },
32
+ ],
33
+ loops: [],
34
+ },
35
+ {
36
+ id: "t02_i_received_not_command",
37
+ from: "Connected",
38
+ on: "I_received",
39
+ guard: "not command",
40
+ actions: [
41
+ { verb: "DL_ERROR_indication_O", kind: "signal_upper" },
42
+ { verb: "discard_I_frame", kind: "processing" },
43
+ ],
44
+ next: "Connected",
45
+ notes: "I frame received as a response rather than a command — protocol\nerror. DL-ERROR(O), discard, stay.\n",
46
+ references: [
47
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDIFRM", line: 2641, note: "in-sequence path; LinBPQ funnels all I-frame logic through SDIFRM → PROC_I_FRAME → no spec_prose-mapped command check" },
48
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame", line: 2648, note: "state_3/state_4 entry. States 3 & 4 share body — divergence from figure" },
49
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1553, note: "Author rejects spec's DlError::O on non-command I: \"not even remotely correct, since O means packet too big\" — emits DlError::S instead" },
50
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 254, note: "no separate command-check; non-I frames fall to default (silent drop)" },
51
+ ],
52
+ loops: [],
53
+ },
54
+ {
55
+ id: "t03_i_received_command_info_field_invalid_v22",
56
+ from: "Connected",
57
+ on: "I_received",
58
+ guard: "command and not info_field_valid and version_2_2",
59
+ actions: [
60
+ { verb: "DL_ERROR_indication_O", kind: "signal_upper" },
61
+ { verb: "Establish_Data_Link", kind: "subroutine" },
62
+ { verb: "clear_layer_3_initiated", kind: "processing" },
63
+ ],
64
+ next: "AwaitingConnection22",
65
+ notes: "Bad I-frame info field (length > N1 or not octet-aligned), v2.2 peer.\nRe-establish link via the v2.2 path.\n",
66
+ references: [
67
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame", line: 2754, note: "bad-info-length path: DL-ERROR(O) message, establish_data_link, state→1 or 5 (modulo-128 author enhancement)" },
68
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1561, note: "Payload>N1 → establish_data_link + DlError::O + AwaitingConnection. Author TODO: \"should we discard it?\"" },
69
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "PROC_I_FRAME", line: 2914, note: "Length<1 or >257 → silent ReleaseBuffer. No DL-ERROR(O), no Establish — diverges from figure" },
70
+ ],
71
+ loops: [],
72
+ },
73
+ {
74
+ id: "t04_i_received_command_info_field_invalid_v20",
75
+ from: "Connected",
76
+ on: "I_received",
77
+ guard: "command and not info_field_valid and not version_2_2",
78
+ actions: [
79
+ { verb: "DL_ERROR_indication_O", kind: "signal_upper" },
80
+ { verb: "Establish_Data_Link", kind: "subroutine" },
81
+ { verb: "clear_layer_3_initiated", kind: "processing" },
82
+ ],
83
+ next: "AwaitingConnection",
84
+ notes: "Bad I-frame info field, v2.0 peer. Re-establish via v2.0 path.\n",
85
+ references: [
86
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame", line: 2754, note: "same code path as t03 — direwolf has no v2.0/v2.2 split for bad info length" },
87
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1561, note: "same as t03" },
88
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "PROC_I_FRAME", line: 2914, note: "same as t03 — LinBPQ has no v22/v20 distinction for info-field length" },
89
+ ],
90
+ loops: [],
91
+ },
92
+ {
93
+ id: "t05_i_received_command_info_valid_nr_out_of_window_v22",
94
+ from: "Connected",
95
+ on: "I_received",
96
+ guard: "command and info_field_valid and not V_a_le_N_r_le_V_s and version_2_2",
97
+ actions: [
98
+ { verb: "N_r_Error_Recovery", kind: "subroutine" },
99
+ ],
100
+ next: "AwaitingConnection22",
101
+ notes: "N(r) outside the V(a)..V(s) window — peer ACK is invalid. Recover\nvia re-establish on v2.2 path.\n",
102
+ references: [
103
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDNRCHK", line: 2968, note: "LinBPQ collapse: all N(r) errors → SDFRMR/FRMR state. No N(r) Error Recovery → AwaitingConnection22" },
104
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame", line: 2747, note: "is_good_nr false → nr_error_recovery, → state 1 or 5 (author enhancement: modulo==128 ? state_5 : state_1)" },
105
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1571, note: "N(R) out of range → nr_error_recovery + transition AwaitingConnection" },
106
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 209, note: "ax25_validate_nr fails → nr_error_recovery + STATE_1" },
107
+ ],
108
+ loops: [],
109
+ },
110
+ {
111
+ id: "t06_i_received_command_info_valid_nr_out_of_window_v20",
112
+ from: "Connected",
113
+ on: "I_received",
114
+ guard: "command and info_field_valid and not V_a_le_N_r_le_V_s and not version_2_2",
115
+ actions: [
116
+ { verb: "N_r_Error_Recovery", kind: "subroutine" },
117
+ ],
118
+ next: "AwaitingConnection",
119
+ notes: "As t05 but v2.0 peer.\n",
120
+ references: [
121
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDNRCHK", line: 2968, note: "same path as t05 — no v22/v20 split" },
122
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame", line: 2747, note: "same as t05" },
123
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1571, note: "same as t05" },
124
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 209, note: "same as t05" },
125
+ ],
126
+ loops: [],
127
+ },
128
+ {
129
+ id: "t07_i_received_own_busy_p_eq_1",
130
+ from: "Connected",
131
+ on: "I_received",
132
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and own_receiver_busy and P_eq_1",
133
+ actions: [
134
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
135
+ { verb: "discard_contents_of_I_frame", kind: "processing" },
136
+ { verb: "F := 1", kind: "processing" },
137
+ { verb: "N(r) := V(r)", kind: "processing" },
138
+ { verb: "RR", kind: "signal_lower" },
139
+ { verb: "clear_acknowledge_pending", kind: "processing" },
140
+ ],
141
+ next: "Connected",
142
+ notes: "Own receiver is busy — drop the I-frame's contents. Peer polled\n(P=1) so we owe a status response: RR with F=1.\n",
143
+ references: [
144
+ { source: "spec_prose", cite: "§6.4.2.2", quote: "If the TNC is in a busy condition, it ignores any received I frames without reporting this condition, other than repeating the indication of the busy condition.", path: "", function: "", line: 0, note: "" },
145
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame", line: 2711, note: "own_receiver_busy && p==1: send RNR response. Author flags \"should be unreachable because we currently don't have a way to set own_receiver_busy\" — figure path effectively dead in direwolf" },
146
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1604, note: "own_receiver_busy: discard iframe (implicit), optionally send RNR if polled. Author note \"discord (implicit)\" typo" },
147
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 219, note: "OWN_RX_BUSY: discard, send enquiry_response only if P=1, no V(r) advance" },
148
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "RR_OR_RNR", line: 4173, note: "no separate own-busy-discard inside SDIFRM — busy decided dynamically at response-send time (QCOUNT<20)" },
149
+ ],
150
+ loops: [],
151
+ },
152
+ {
153
+ id: "t08_i_received_own_busy_p_eq_0",
154
+ from: "Connected",
155
+ on: "I_received",
156
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and own_receiver_busy and not P_eq_1",
157
+ actions: [
158
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
159
+ { verb: "discard_contents_of_I_frame", kind: "processing" },
160
+ ],
161
+ next: "Connected",
162
+ notes: "Own busy and not polled — just drop the frame, stay.\n",
163
+ references: [
164
+ { source: "spec_prose", cite: "§6.4.2.2", quote: "If the TNC is in a busy condition, it ignores any received I frames without reporting this condition, other than repeating the indication of the busy condition.", path: "", function: "", line: 0, note: "" },
165
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame", line: 2707, note: "own_receiver_busy with !P=1: implicit no-op. See t07 note about unreachable code." },
166
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 219, note: "same OWN_RX_BUSY branch; P=0 takes the no-response path" },
167
+ ],
168
+ loops: [],
169
+ },
170
+ {
171
+ id: "t09_i_received_in_seq_no_stored_p_eq_1",
172
+ from: "Connected",
173
+ on: "I_received",
174
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and N_s_eq_V_r and not V_r_I_frame_stored and P_eq_1",
175
+ actions: [
176
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
177
+ { verb: "V(r) := V(r) + 1", kind: "processing" },
178
+ { verb: "clear_reject_exception", kind: "processing" },
179
+ { verb: "decrement_srej_exception_if_gt_0", kind: "processing" },
180
+ { verb: "DL_DATA_indication", kind: "signal_upper" },
181
+ { verb: "F := 1", kind: "processing" },
182
+ { verb: "N(r) := V(r)", kind: "processing" },
183
+ { verb: "RR", kind: "signal_lower" },
184
+ { verb: "clear_acknowledge_pending", kind: "processing" },
185
+ ],
186
+ next: "Connected",
187
+ notes: "In-sequence I frame received, no stored frame for V(r), peer\npolled. Deliver data, advance V(r), respond RR(F=1).\n",
188
+ references: [
189
+ { source: "spec_prose", cite: "§6.4.2.1", quote: "If a TNC receives a valid I frame...it accepts the received I frame, increments its receive state variable, and acts in one of the following manners: …the receiving TNC sends an RR frame with N(R) equal to V(R).", path: "", function: "", line: 0, note: "" },
190
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 2927, note: "P=1 path: send RR response with F=1 after in-order delivery" },
191
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1635, note: "After in-order delivery with poll: send RR F=1, clear acknowledge_pending" },
192
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 229, note: "ns==vr with pf=1: send enquiry_response (RR/RNR F=1) instead of arming T2" },
193
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDIFRM", line: 2688, note: "P-bit set → SEND_RR_RESP(LINK, PFBIT) at 2708" },
194
+ ],
195
+ loops: [],
196
+ },
197
+ {
198
+ id: "t10_i_received_in_seq_no_stored_p_eq_0_ack_pending",
199
+ from: "Connected",
200
+ on: "I_received",
201
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and N_s_eq_V_r and not V_r_I_frame_stored and not P_eq_1 and acknowledge_pending",
202
+ actions: [
203
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
204
+ { verb: "V(r) := V(r) + 1", kind: "processing" },
205
+ { verb: "clear_reject_exception", kind: "processing" },
206
+ { verb: "decrement_srej_exception_if_gt_0", kind: "processing" },
207
+ { verb: "DL_DATA_indication", kind: "signal_upper" },
208
+ ],
209
+ next: "Connected",
210
+ notes: "In-sequence data; not polled; ack already pending — let it batch.\n",
211
+ references: [
212
+ { source: "spec_prose", cite: "§6.4.2.1", quote: "If there are no outstanding I frames, the receiving TNC sends an RR frame with N(R) equal to V(R). The receiving TNC may wait a small period of time before sending the RR frame to be sure additional I frames are not being transmitted.", path: "", function: "", line: 0, note: "" },
213
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 2956, note: "P=0, ack_pending: no-op; will piggyback or T2-fire later" },
214
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1644, note: "In-order, no poll: set acknowledge_pending. Comment \"LM seize request (?)\"" },
215
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "PROC_I_FRAME", line: 2930, note: "Sets L2ACKREQ = PORTT2 (delayed-ack) — equivalent to ack-pending; sent later via L2TimerProc:3533" },
216
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 232, note: "ACK_PENDING set + T2 started when pf=0 — T2 is Linux's deferred-ack timer (figc4.4 LM-SEIZE-request analogue)" },
217
+ ],
218
+ loops: [],
219
+ },
220
+ {
221
+ id: "t11_i_received_in_seq_no_stored_p_eq_0_no_ack_pending",
222
+ from: "Connected",
223
+ on: "I_received",
224
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and N_s_eq_V_r and not V_r_I_frame_stored and not P_eq_1 and not acknowledge_pending",
225
+ actions: [
226
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
227
+ { verb: "V(r) := V(r) + 1", kind: "processing" },
228
+ { verb: "clear_reject_exception", kind: "processing" },
229
+ { verb: "decrement_srej_exception_if_gt_0", kind: "processing" },
230
+ { verb: "DL_DATA_indication", kind: "signal_upper" },
231
+ { verb: "LM_seize_request", kind: "signal_lower" },
232
+ { verb: "set_acknowledge_pending", kind: "processing" },
233
+ ],
234
+ next: "Connected",
235
+ notes: "In-sequence data; not polled; no ack pending — arrange to send\none: seize the medium and set ack-pending so we know to RR when\nwe get LM-SEIZE Confirm (see column 24).\n",
236
+ references: [
237
+ { source: "spec_prose", cite: "§6.4.2.1", quote: "If there are no outstanding I frames, the receiving TNC sends an RR frame with N(R) equal to V(R). The receiving TNC may wait a small period of time before sending the RR frame…", path: "", function: "", line: 0, note: "" },
238
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 2942, note: "!ack_pending branch: set ack_pending=1, lm_seize_request" },
239
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1644, note: "same handler as t10 — ack_pending flag set but no concrete LM seize emitted" },
240
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 232, note: "same path as t10 via T2 timer" },
241
+ ],
242
+ loops: [],
243
+ },
244
+ {
245
+ id: "t12_i_received_out_of_seq_reject_exception_p_eq_1",
246
+ from: "Connected",
247
+ on: "I_received",
248
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and not N_s_eq_V_r and reject_exception and P_eq_1",
249
+ actions: [
250
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
251
+ { verb: "discard_contents_of_I_frame", kind: "processing" },
252
+ { verb: "F := 1", kind: "processing" },
253
+ { verb: "N(r) := V(r)", kind: "processing" },
254
+ { verb: "RR", kind: "signal_lower" },
255
+ { verb: "clear_acknowledge_pending", kind: "processing" },
256
+ ],
257
+ next: "Connected",
258
+ notes: "",
259
+ references: [
260
+ { source: "spec_prose", cite: "§6.4.4.1", quote: "When an I frame is received with a correct FCS, but its send sequence number N(S) does not match the current receiver's receive state variable, the frame is discarded.", path: "", function: "", line: 0, note: "" },
261
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 2974, note: "srej_enable==srej_none path: set reject_exception, REJ response, F=P" },
262
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1651, note: "reject_exception already set: discard frame; respond RR only if polled" },
263
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDIFRM", line: 2670, note: "L2STATE==6 (REJ Sent) drops out of REJ when in-sequence frame arrives" },
264
+ ],
265
+ loops: [],
266
+ },
267
+ {
268
+ id: "t13_i_received_out_of_seq_reject_exception_p_eq_0",
269
+ from: "Connected",
270
+ on: "I_received",
271
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and not N_s_eq_V_r and reject_exception and not P_eq_1",
272
+ actions: [
273
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
274
+ { verb: "discard_contents_of_I_frame", kind: "processing" },
275
+ ],
276
+ next: "Connected",
277
+ notes: "",
278
+ references: [
279
+ { source: "spec_prose", cite: "§6.4.4.1", quote: "When an I frame is received with a correct FCS, but its send sequence number N(S) does not match the current receiver's receive state variable, the frame is discarded.", path: "", function: "", line: 0, note: "" },
280
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 2956, note: "REJ outstanding + P=0: no response, stay" },
281
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1651, note: "same handler as t12" },
282
+ ],
283
+ loops: [],
284
+ },
285
+ {
286
+ id: "t14_i_received_out_of_seq_srej_enabled_no_excep_in_range",
287
+ from: "Connected",
288
+ on: "I_received",
289
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and not N_s_eq_V_r and not reject_exception and srej_enabled and not srej_exception_gt_0 and not N_s_gt_V_r_plus_1",
290
+ actions: [
291
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
292
+ { verb: "save_contents_of_I_frame", kind: "processing" },
293
+ { verb: "N(r) := V(r)", kind: "processing" },
294
+ { verb: "F := 1", kind: "processing" },
295
+ { verb: "increment_srej_exception", kind: "processing" },
296
+ { verb: "SREJ", kind: "signal_lower" },
297
+ ],
298
+ next: "Connected",
299
+ notes: "",
300
+ references: [
301
+ { source: "spec_prose", cite: "§6.4.4.2", quote: "When an I frame is received…the frame is retained. SREJ frames are sent with a receive sequence number equal to the value N(R) of the missing frame, and P=1 if an uncleared SREJ condition has not been previously established.", path: "", function: "", line: 0, note: "" },
302
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 3018, note: "SREJ-enabled and is_ns_in_window. Author Erratum 3014: \"AX.25 protocol spec did not handle SREJ very well. Based on X.25 section 2.4.6.4.\" Major divergence — direwolf follows X.25 rather than AX.25 SDL" },
303
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1697, note: "Single-frame gap with SREJ enabled: sreject_exception++, ack_pending=false. TODO: SendSrej action not implemented" },
304
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDIFRM", line: 2636, note: "Out-of-sequence stored in RXFRAMES[NS]; SREJ vs REJ chosen in RR_OR_RNR:4230" },
305
+ ],
306
+ loops: [],
307
+ },
308
+ {
309
+ id: "t15_i_received_out_of_seq_srej_enabled_no_excep_far_skip",
310
+ from: "Connected",
311
+ on: "I_received",
312
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and not N_s_eq_V_r and not reject_exception and srej_enabled and not srej_exception_gt_0 and N_s_gt_V_r_plus_1",
313
+ actions: [
314
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
315
+ { verb: "save_contents_of_I_frame", kind: "processing" },
316
+ { verb: "discard_contents_of_I_frame", kind: "processing" },
317
+ { verb: "set_reject_exception", kind: "processing" },
318
+ { verb: "F := P", kind: "processing" },
319
+ { verb: "N(r) := V(r)", kind: "processing" },
320
+ { verb: "REJ", kind: "signal_lower" },
321
+ { verb: "clear_acknowledge_pending", kind: "processing" },
322
+ ],
323
+ next: "Connected",
324
+ notes: "",
325
+ references: [
326
+ { source: "spec_prose", cite: "§6.4.4.2", quote: "SREJ frames are sent with a receive sequence number equal to the value N(R) of the missing frame, and P=1 if an uncleared SREJ condition has not been previously established.", path: "", function: "", line: 0, note: "" },
327
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 3102, note: "X.25 2.4.6.4 (a) — N(S) out of window, discard, respond if P=1" },
328
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1688, note: "p.ns != (vr+1) % modulus: SendRej path (gap > 1). Comment \"Maybe a version of if in_range(p.ns)\" TODO" },
329
+ ],
330
+ loops: [],
331
+ },
332
+ {
333
+ id: "t16_i_received_out_of_seq_srej_enabled_existing_excep",
334
+ from: "Connected",
335
+ on: "I_received",
336
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and not N_s_eq_V_r and not reject_exception and srej_enabled and srej_exception_gt_0",
337
+ actions: [
338
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
339
+ { verb: "save_contents_of_I_frame", kind: "processing" },
340
+ { verb: "N(r) := N(s)", kind: "processing" },
341
+ { verb: "F := 0", kind: "processing" },
342
+ { verb: "increment_srej_exception", kind: "processing" },
343
+ { verb: "SREJ", kind: "signal_lower" },
344
+ ],
345
+ next: "Connected",
346
+ notes: "",
347
+ references: [
348
+ { source: "spec_prose", cite: "§6.4.4.2", quote: "If an SREJ condition is already pending, an SREJ will be sent with P=0.", path: "", function: "", line: 0, note: "" },
349
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 3018, note: "same SREJ-in-window path as t14 with existing exception" },
350
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1680, note: "sreject_exception > 0: increment + clear ack_pending. TODO: actual SendSrej not implemented" },
351
+ ],
352
+ loops: [],
353
+ },
354
+ {
355
+ id: "t17_i_received_out_of_seq_srej_disabled",
356
+ from: "Connected",
357
+ on: "I_received",
358
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and not N_s_eq_V_r and not reject_exception and not srej_enabled",
359
+ actions: [
360
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
361
+ { verb: "discard_contents_of_I_frame", kind: "processing" },
362
+ { verb: "set_reject_exception", kind: "processing" },
363
+ { verb: "F := P", kind: "processing" },
364
+ { verb: "N(r) := V(r)", kind: "processing" },
365
+ { verb: "REJ", kind: "signal_lower" },
366
+ { verb: "clear_acknowledge_pending", kind: "processing" },
367
+ ],
368
+ next: "Connected",
369
+ notes: "",
370
+ references: [
371
+ { source: "spec_prose", cite: "§6.4.4.1", quote: "A REJ frame is sent with a receive sequence number equal to one higher than the last correctly received I frame if an uncleared N(S) sequence error condition has not been previously established.", path: "", function: "", line: 0, note: "" },
372
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 2974, note: "SREJ disabled: set reject_exception, REJ response, F=P. Same as t12" },
373
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1663, note: "SREJ disabled path: set reject_exception, SendRej. Author TODO: \"should we maybe wait a bit with sending a REJ?\"" },
374
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDIFRM", line: 2605, note: "L2OUTOFSEQ++; L2STATE=6 (REJ state). REJ sent later when L2ACKREQ expires" },
375
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 242, note: "REJ F=pf RESPONSE on first out-of-sequence I, ACK_PENDING cleared. Linux: go-back-N only — no SREJ alternative" },
376
+ ],
377
+ loops: [],
378
+ },
379
+ {
380
+ id: "t18_dl_data_request",
381
+ from: "Connected",
382
+ on: "DL_DATA_request",
383
+ guard: "",
384
+ actions: [
385
+ { verb: "push_on_I_frame_queue", kind: "internal_out" },
386
+ ],
387
+ next: "Connected",
388
+ notes: "verification_pending: graphml label is 'Push on I Frame Queue\n(note: word order?)' — Tom flagged uncertainty about whether the\nspec text is \"Push on I Frame Queue\" vs \"Push I Frame on Queue\"\n(or similar word order). Canonical verb chosen here is\npush_on_I_frame_queue. Worth a spec re-read.\n",
389
+ references: [
390
+ { source: "spec_prose", cite: "§6.4.1", quote: "Whenever a TNC has an I frame to transmit, it sends the I frame with the N(S) of the control field equal to its current send state variable V(S).", path: "", function: "", line: 0, note: "" },
391
+ { source: "linbpq", cite: "", quote: "", path: "L4Code.c", function: "SendL4Data", line: 919, note: "C_Q_ADD(&LINK->TX_Q, Msg). Multiple L4 paths (637/908/919/1846/1950/1980) all push to TX_Q" },
392
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "data_request_good_size", line: 1480, note: "state_3/state_4 fall-through: append to I-frame queue, then lm_seize_request if within window. v1.5 change at 1502-1506" },
393
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::data", line: 1491, note: "Appends to obuf — \"deliberately doesn't preserve the application's frame boundaries\" — then calls Data::flush()" },
394
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/af_ax25.c", function: "ax25_sendmsg", line: 1630, note: "SOCK_SEQPACKET path: ax25_output() → ax25_kick(). No explicit DL-DATA primitive" },
395
+ ],
396
+ loops: [],
397
+ },
398
+ {
399
+ id: "t19_i_frame_pops_off_queue_send_now_t1_running",
400
+ from: "Connected",
401
+ on: "I_frame_pops_off_queue",
402
+ guard: "not peer_receiver_busy and not V_s_eq_V_a_plus_k and T1_running",
403
+ actions: [
404
+ { verb: "N(s) := V(s)", kind: "processing" },
405
+ { verb: "N(r) := V(r)", kind: "processing" },
406
+ { verb: "p := 0", kind: "processing" },
407
+ { verb: "I_command", kind: "signal_lower" },
408
+ { verb: "V(s) := V(s) + 1", kind: "processing" },
409
+ { verb: "clear_acknowledge_pending", kind: "processing" },
410
+ ],
411
+ next: "Connected",
412
+ notes: "",
413
+ references: [
414
+ { source: "spec_prose", cite: "§6.4.1", quote: "After the I frame is sent, the send state variable is incremented by one. If timer T1 is not running, it is started. If timer T1 is running, it is restarted.", path: "", function: "", line: 0, note: "" },
415
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_pop_off_queue", line: 6591, note: "Build I frame, V(s)++, ack_pending=0. Author erratum 6621: \"always want to restart T1 when an I frame is sent\" — unconditional STOP_T3/START_T1" },
416
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Data::flush", line: 882, note: "Drains mtu_out bytes, builds Iframe, vs++, emits SendIframe. TODO on Direwolf advice \"always restart T1\"" },
417
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_out.c", function: "ax25_kick", line: 251, note: "Window-bounded TX loop. Sends with P=1 only on last frame of window (308). Stops T3 / starts T1 at end (330-332). Handles t19-t22 uniformly" },
418
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDETX", line: 3195, note: "Drains TX_Q, sends from FRAMES[LINKNS], V(s)++. T1 reset to ONEMINUTE unconditionally — no distinction t19/t20" },
419
+ ],
420
+ loops: [],
421
+ },
422
+ {
423
+ id: "t20_i_frame_pops_off_queue_send_now_t1_not_running",
424
+ from: "Connected",
425
+ on: "I_frame_pops_off_queue",
426
+ guard: "not peer_receiver_busy and not V_s_eq_V_a_plus_k and not T1_running",
427
+ actions: [
428
+ { verb: "N(s) := V(s)", kind: "processing" },
429
+ { verb: "N(r) := V(r)", kind: "processing" },
430
+ { verb: "p := 0", kind: "processing" },
431
+ { verb: "I_command", kind: "signal_lower" },
432
+ { verb: "V(s) := V(s) + 1", kind: "processing" },
433
+ { verb: "clear_acknowledge_pending", kind: "processing" },
434
+ { verb: "stop_T3", kind: "processing" },
435
+ { verb: "start_T1", kind: "processing" },
436
+ ],
437
+ next: "Connected",
438
+ notes: "",
439
+ references: [
440
+ { source: "spec_prose", cite: "§6.4.1", quote: "If timer T1 is not running, it is started. If timer T1 is running, it is restarted.", path: "", function: "", line: 0, note: "" },
441
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_pop_off_queue", line: 6624, note: "unconditional STOP_T3/START_T1 (covers t19 and t20)" },
442
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Data::flush", line: 891, note: "if T1 not running: stop T3, start T1(srt)" },
443
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDETX", line: 3431, note: "same as t19 — no T1-state branch" },
444
+ ],
445
+ loops: [],
446
+ },
447
+ {
448
+ id: "t21_i_frame_pops_off_queue_window_full",
449
+ from: "Connected",
450
+ on: "I_frame_pops_off_queue",
451
+ guard: "not peer_receiver_busy and V_s_eq_V_a_plus_k",
452
+ actions: [
453
+ { verb: "push_on_I_frame_queue", kind: "internal_out" },
454
+ ],
455
+ next: "Connected",
456
+ notes: "Window full (V(s) reached V(a)+k) — push back on queue and wait.\n",
457
+ references: [
458
+ { source: "spec_prose", cite: "§6.4.1", quote: "The TNC does not transmit any more I frames if its send state variable equals the last received N(R) from the other side of the link plus k. If the TNC sent more I frames, the flow control window would be exceeded.", path: "", function: "", line: 0, note: "" },
459
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_pop_off_queue", line: 6581, note: "while-loop guard exits when window full (WITHIN_WINDOW_SIZE false)" },
460
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Data::flush", line: 875, note: "vs == (va+k) mod modulus: window full, leaves obuf intact" },
461
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDETX", line: 3190, note: "Window check Outstanding >= LINKWINDOW; frames stay on TX_Q. No \"push back\" — never popped" },
462
+ ],
463
+ loops: [],
464
+ },
465
+ {
466
+ id: "t22_i_frame_pops_off_queue_peer_busy",
467
+ from: "Connected",
468
+ on: "I_frame_pops_off_queue",
469
+ guard: "peer_receiver_busy",
470
+ actions: [
471
+ { verb: "push_on_I_frame_queue", kind: "internal_out" },
472
+ ],
473
+ next: "Connected",
474
+ notes: "Peer busy — push back on queue.\n",
475
+ references: [
476
+ { source: "spec_prose", cite: "§6.4.1", quote: "If a TNC is in a busy condition, it may still send I frames as long as the distant TNC is not also busy.", path: "", function: "", line: 0, note: "" },
477
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_pop_off_queue", line: 6581, note: "same loop guard handles peer_busy" },
478
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Data::flush", line: 867, note: "Peer receiver busy → returns empty (no send)" },
479
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_out.c", function: "ax25_kick", line: 260, note: "PEER_RX_BUSY guard at line 260; loop skips" },
480
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDETX", line: 3360, note: "send loop guarded by POLLSENT not peer-busy; RNRSET consulted via timer policy" },
481
+ ],
482
+ loops: [],
483
+ },
484
+ {
485
+ id: "t23_dl_unit_data_request",
486
+ from: "Connected",
487
+ on: "DL_UNIT_DATA_request",
488
+ guard: "",
489
+ actions: [
490
+ { verb: "UI_command", kind: "signal_lower" },
491
+ ],
492
+ next: "Connected",
493
+ notes: "",
494
+ references: [
495
+ { source: "spec_prose", cite: "§6.3.7", quote: "AX.25 uses a special frame for this operation, the Unnumbered Information (UI) frame.", path: "", function: "", line: 0, note: "" },
496
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/af_ax25.c", function: "ax25_sendmsg", line: 1645, note: "SOCK_DGRAM path: builds UI directly, bypasses LAPB state machine. Same path in any state" },
497
+ ],
498
+ loops: [],
499
+ },
500
+ {
501
+ id: "t24_dl_flow_off_when_not_busy",
502
+ from: "Connected",
503
+ on: "DL_FLOW_OFF_request",
504
+ guard: "own_receiver_busy",
505
+ actions: [
506
+ { verb: "set_own_receiver_busy", kind: "processing" },
507
+ { verb: "RNR_response", kind: "signal_lower" },
508
+ { verb: "clear_acknowledge_pending", kind: "processing" },
509
+ ],
510
+ next: "Connected",
511
+ notes: "",
512
+ references: [
513
+ { source: "spec_prose", cite: "§6.4.10", quote: "Whenever a TNC enters a busy condition, it indicates this by sending an RNR response at the next opportunity.", path: "", function: "", line: 0, note: "" },
514
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "RR_OR_RNR", line: 4238, note: "RNR decided dynamically at supervisory-send time (QCOUNT<20 or session busy). No explicit DL-FLOW-OFF primitive" },
515
+ ],
516
+ loops: [],
517
+ },
518
+ {
519
+ id: "t25_dl_flow_off_when_already_busy",
520
+ from: "Connected",
521
+ on: "DL_FLOW_OFF_request",
522
+ guard: "not own_receiver_busy",
523
+ actions: [],
524
+ next: "Connected",
525
+ notes: "",
526
+ references: [],
527
+ loops: [],
528
+ },
529
+ {
530
+ id: "t26_dl_flow_on_when_busy_and_t1_not_running",
531
+ from: "Connected",
532
+ on: "DL_FLOW_ON_request",
533
+ guard: "own_receiver_busy and not T1_running",
534
+ actions: [
535
+ { verb: "clear_own_receiver_busy", kind: "processing" },
536
+ { verb: "RR_command", kind: "signal_lower" },
537
+ { verb: "clear_acknowledge_pending", kind: "processing" },
538
+ { verb: "stop_T3", kind: "processing" },
539
+ { verb: "start_T1", kind: "processing" },
540
+ ],
541
+ next: "Connected",
542
+ notes: "",
543
+ references: [
544
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2TimerProc", line: 3505, note: "RNRSENT clears when no longer busy → SendSupervisCmd; closest analog to DL-FLOW-ON" },
545
+ ],
546
+ loops: [],
547
+ },
548
+ {
549
+ id: "t27_dl_flow_on_when_busy_and_t1_running",
550
+ from: "Connected",
551
+ on: "DL_FLOW_ON_request",
552
+ guard: "own_receiver_busy and T1_running",
553
+ actions: [
554
+ { verb: "clear_own_receiver_busy", kind: "processing" },
555
+ { verb: "RR_command", kind: "signal_lower" },
556
+ { verb: "clear_acknowledge_pending", kind: "processing" },
557
+ ],
558
+ next: "Connected",
559
+ notes: "",
560
+ references: [],
561
+ loops: [],
562
+ },
563
+ {
564
+ id: "t28_dl_flow_on_when_not_busy",
565
+ from: "Connected",
566
+ on: "DL_FLOW_ON_request",
567
+ guard: "not own_receiver_busy",
568
+ actions: [],
569
+ next: "Connected",
570
+ notes: "",
571
+ references: [
572
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SendSupervisCmd", line: 3621, note: "RR(P) sent with PFBIT, L2TIMER restart via L2SENDCOMMAND" },
573
+ ],
574
+ loops: [],
575
+ },
576
+ {
577
+ id: "t29_dl_connect_request_v22",
578
+ from: "Connected",
579
+ on: "DL_CONNECT_request",
580
+ guard: "version_2_2",
581
+ actions: [
582
+ { verb: "discard_I_frame_queue", kind: "processing" },
583
+ { verb: "Establish_Data_Link", kind: "subroutine" },
584
+ { verb: "set_layer_3_initiated", kind: "processing" },
585
+ ],
586
+ next: "AwaitingConnection22",
587
+ notes: "",
588
+ references: [
589
+ { source: "spec_prose", cite: "§6.3.6.2", quote: "If sent and received SABM(E) or DISC command frames are the same, both TNCs send a UA response at the earliest opportunity, and both devices enter the indicated state.", path: "", function: "", line: 0, note: "" },
590
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "dl_connect_request", line: 1052, note: "state_3/state_4 fall-through: discard_i_queue, establish_data_link, layer_3_initiated=1. Author enhancement at 1058-1060: state_5 if modulo==128 else state_1" },
591
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/af_ax25.c", function: "ax25_connect", line: 1227, note: "Already STATE_3: rejects with -EISCONN. Linux does NOT implement re-establishment" },
592
+ ],
593
+ loops: [],
594
+ },
595
+ {
596
+ id: "t30_dl_connect_request_v20",
597
+ from: "Connected",
598
+ on: "DL_CONNECT_request",
599
+ guard: "not version_2_2",
600
+ actions: [
601
+ { verb: "discard_I_frame_queue", kind: "processing" },
602
+ { verb: "Establish_Data_Link", kind: "subroutine" },
603
+ { verb: "set_layer_3_initiated", kind: "processing" },
604
+ ],
605
+ next: "AwaitingConnection",
606
+ notes: "",
607
+ references: [
608
+ { source: "spec_prose", cite: "§6.3.6.2", quote: "If sent and received SABM(E) or DISC command frames are the same, both TNCs send a UA response at the earliest opportunity, and both devices enter the indicated state.", path: "", function: "", line: 0, note: "" },
609
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "dl_connect_request", line: 1060, note: "modulo!=128 branch → state_1" },
610
+ ],
611
+ loops: [],
612
+ },
613
+ {
614
+ id: "t31_all_other_primitives_from_lower_layer",
615
+ from: "Connected",
616
+ on: "all_other_primitives__from_lower_layer",
617
+ guard: "",
618
+ actions: [],
619
+ next: "Connected",
620
+ notes: "",
621
+ references: [
622
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2_PROCESS", line: 2203, note: "default → ReleaseBuffer" },
623
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 254, note: "default case silently no-ops" },
624
+ ],
625
+ loops: [],
626
+ },
627
+ {
628
+ id: "t32_control_field_error_v22",
629
+ from: "Connected",
630
+ on: "control_field_error",
631
+ guard: "version_2_2",
632
+ actions: [
633
+ { verb: "DL_ERROR_indication_L", kind: "signal_upper" },
634
+ { verb: "discard_I_frame_queue", kind: "processing" },
635
+ { verb: "Establish_Data_Link", kind: "subroutine" },
636
+ { verb: "set_layer_3_initiated", kind: "processing" },
637
+ ],
638
+ next: "AwaitingConnection22",
639
+ notes: "",
640
+ references: [
641
+ { source: "spec_prose", cite: "§6.4.5", quote: "When a TNC receives a frame with an incorrect FCS, an invalid frame, or a frame with an improper address, that frame is discarded.", path: "", function: "", line: 0, note: "" },
642
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2_PROCESS", line: 2049, note: "default in S-frame switch → SDFRMR (FRMR state). NO Establish Data Link — sends FRMR instead" },
643
+ ],
644
+ loops: [],
645
+ },
646
+ {
647
+ id: "t33_control_field_error_v20",
648
+ from: "Connected",
649
+ on: "control_field_error",
650
+ guard: "not version_2_2",
651
+ actions: [
652
+ { verb: "DL_ERROR_indication_L", kind: "signal_upper" },
653
+ { verb: "discard_I_frame_queue", kind: "processing" },
654
+ { verb: "Establish_Data_Link", kind: "subroutine" },
655
+ { verb: "set_layer_3_initiated", kind: "processing" },
656
+ ],
657
+ next: "AwaitingConnection",
658
+ notes: "",
659
+ references: [
660
+ { source: "spec_prose", cite: "§6.4.5", quote: "When a TNC receives a frame with an incorrect FCS, an invalid frame, or a frame with an improper address, that frame is discarded.", path: "", function: "", line: 0, note: "" },
661
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2_PROCESS", line: 2049, note: "same as t32 — no v22/v20 split" },
662
+ ],
663
+ loops: [],
664
+ },
665
+ {
666
+ id: "t34_info_not_permitted_in_frame_v22",
667
+ from: "Connected",
668
+ on: "info_not_permitted_in_frame",
669
+ guard: "version_2_2",
670
+ actions: [
671
+ { verb: "DL_ERROR_indication_M", kind: "signal_upper" },
672
+ { verb: "discard_I_frame_queue", kind: "processing" },
673
+ { verb: "Establish_Data_Link", kind: "subroutine" },
674
+ { verb: "set_layer_3_initiated", kind: "processing" },
675
+ ],
676
+ next: "AwaitingConnection22",
677
+ notes: "",
678
+ references: [
679
+ { source: "spec_prose", cite: "§6.4.5", quote: "When a TNC receives a frame with an incorrect FCS, an invalid frame, or a frame with an improper address, that frame is discarded.", path: "", function: "", line: 0, note: "" },
680
+ ],
681
+ loops: [],
682
+ },
683
+ {
684
+ id: "t35_info_not_permitted_in_frame_v20",
685
+ from: "Connected",
686
+ on: "info_not_permitted_in_frame",
687
+ guard: "not version_2_2",
688
+ actions: [
689
+ { verb: "DL_ERROR_indication_M", kind: "signal_upper" },
690
+ { verb: "discard_I_frame_queue", kind: "processing" },
691
+ { verb: "Establish_Data_Link", kind: "subroutine" },
692
+ { verb: "set_layer_3_initiated", kind: "processing" },
693
+ ],
694
+ next: "AwaitingConnection",
695
+ notes: "",
696
+ references: [
697
+ { source: "spec_prose", cite: "§6.4.5", quote: "When a TNC receives a frame with an incorrect FCS, an invalid frame, or a frame with an improper address, that frame is discarded.", path: "", function: "", line: 0, note: "" },
698
+ ],
699
+ loops: [],
700
+ },
701
+ {
702
+ id: "t36_u_or_s_frame_length_error_v22",
703
+ from: "Connected",
704
+ on: "u_or_s_frame_length_error",
705
+ guard: "version_2_2",
706
+ actions: [
707
+ { verb: "DL_ERROR_indication_N", kind: "signal_upper" },
708
+ { verb: "discard_I_frame_queue", kind: "processing" },
709
+ { verb: "Establish_Data_Link", kind: "subroutine" },
710
+ { verb: "set_layer_3_initiated", kind: "processing" },
711
+ ],
712
+ next: "AwaitingConnection22",
713
+ notes: "",
714
+ references: [
715
+ { source: "spec_prose", cite: "§6.4.5", quote: "When a TNC receives a frame with an incorrect FCS, an invalid frame, or a frame with an improper address, that frame is discarded.", path: "", function: "", line: 0, note: "" },
716
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "PROC_I_FRAME", line: 2914, note: "Length<1 or >257 → drop. No DL-ERROR(N)" },
717
+ ],
718
+ loops: [],
719
+ },
720
+ {
721
+ id: "t37_u_or_s_frame_length_error_v20",
722
+ from: "Connected",
723
+ on: "u_or_s_frame_length_error",
724
+ guard: "not version_2_2",
725
+ actions: [
726
+ { verb: "DL_ERROR_indication_N", kind: "signal_upper" },
727
+ { verb: "discard_I_frame_queue", kind: "processing" },
728
+ { verb: "Establish_Data_Link", kind: "subroutine" },
729
+ { verb: "set_layer_3_initiated", kind: "processing" },
730
+ ],
731
+ next: "AwaitingConnection",
732
+ notes: "",
733
+ references: [
734
+ { source: "spec_prose", cite: "§6.4.5", quote: "When a TNC receives a frame with an incorrect FCS, an invalid frame, or a frame with an improper address, that frame is discarded.", path: "", function: "", line: 0, note: "" },
735
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "PROC_I_FRAME", line: 2914, note: "same as t36" },
736
+ ],
737
+ loops: [],
738
+ },
739
+ {
740
+ id: "t38_t1_expiry",
741
+ from: "Connected",
742
+ on: "T1_expiry",
743
+ guard: "",
744
+ actions: [
745
+ { verb: "RC := 1", kind: "processing" },
746
+ { verb: "Transmit_Enquiry", kind: "subroutine" },
747
+ ],
748
+ next: "TimerRecovery",
749
+ notes: "T1 expired — start the timer-recovery procedure. Note: figure\nlabels the subroutine \"Transmit Enquery\" (sic) — preserved\nverbatim but the canonical verb here is the spec's\n\"Transmit Enquiry\" routine.\n",
750
+ references: [
751
+ { source: "spec_prose", cite: "§6.4.11", quote: "If the originating TNC's timer T1 expires while awaiting the distant TNC's acknowledgement of an I frame transmitted, the originating TNC restarts timer T1 and transmits an appropriate supervisory command frame (RR or RNR) with the P bit set.", path: "", function: "", line: 0, note: "" },
752
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2TIMEOUT", line: 3826, note: "State>=5: L2RETRIES++; sends DISC at N2 (3839) or SendSupervisCmd (3843). No separate TimerRecovery state in LinBPQ" },
753
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "t1_expiry", line: 5514, note: "state_3: SET_RC(1), transmit_enquiry, → state_4" },
754
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::t1", line: 1704, note: "Single method handles Connected (rc=1) and TimerRecovery (rc++). Author: \"1998 spec doesn't say if it should be true or false. 2017 spec adds that pf should be false\"" },
755
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_timer.c", function: "ax25_std_t1timer_expiry", line: 155, note: "STATE_3: n2count=1, transmit_enquiry, → STATE_4" },
756
+ ],
757
+ loops: [],
758
+ },
759
+ {
760
+ id: "t39_t3_expiry",
761
+ from: "Connected",
762
+ on: "T3_expiry",
763
+ guard: "",
764
+ actions: [
765
+ { verb: "RC := 1", kind: "processing" },
766
+ { verb: "Transmit_Enquiry", kind: "subroutine" },
767
+ ],
768
+ next: "TimerRecovery",
769
+ notes: "T3 expired — long idle, poll the peer via Transmit Enquiry.\n",
770
+ references: [
771
+ { source: "spec_prose", cite: "§6.7.1", quote: "T3 (inactive link timer): used to monitor data link traffic during idle periods to ensure the link is still functional.", path: "", function: "", line: 0, note: "" },
772
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2TimerProc", line: 3557, note: "L2SLOTIM (T3) decrement → SendSupervisCmd (RR(P) link-validation poll)" },
773
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "t3_expiry", line: 5631, note: "Author erratum 5633: \"Original sets RC to 0, 2006 revision sets RC to 1 which makes more sense.\" Direwolf follows 2006" },
774
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::t3", line: 1732, note: "Author: \"1998 bug: Says to set rc=0. Fixed in 2017.\" Sets rc=1, → TimerRecovery" },
775
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_timer.c", function: "ax25_std_t3timer_expiry", line: 87, note: "n2count=0 then transmit_enquiry, → STATE_4. DIVERGES: figure says \"stay in Connected\"; Linux moves to TimerRecovery" },
776
+ ],
777
+ loops: [],
778
+ },
779
+ {
780
+ id: "t40_sabm_received_vs_neq_va",
781
+ from: "Connected",
782
+ on: "SABM_received",
783
+ guard: "not V_s_eq_V_a",
784
+ actions: [
785
+ { verb: "F := P", kind: "processing" },
786
+ { verb: "set_version_2_0", kind: "processing" },
787
+ { verb: "UA", kind: "signal_lower" },
788
+ { verb: "Clear_Exception_Conditions", kind: "subroutine" },
789
+ { verb: "DL_ERROR_indication_F", kind: "signal_upper" },
790
+ { verb: "discard_I_frame_queue", kind: "processing" },
791
+ { verb: "DL_CONNECT_indication", kind: "signal_upper" },
792
+ { verb: "stop_T1", kind: "processing" },
793
+ { verb: "start_T3", kind: "processing" },
794
+ { verb: "V(a) := 0", kind: "processing" },
795
+ { verb: "V(s) := 0", kind: "processing" },
796
+ { verb: "V(r) := 0", kind: "processing" },
797
+ { verb: "RC := 0", kind: "processing" },
798
+ ],
799
+ next: "Connected",
800
+ notes: "Peer sent SABM while we're already Connected → reset to v2.0.\nV(s) ≠ V(a) means we have unacknowledged data; discard queue and\nnotify L3 of the reset via DL-CONNECT indication.\n",
801
+ references: [
802
+ { source: "spec_prose", cite: "§6.3.3", quote: "If the TNC receives a SABM(E) command while in the information-transfer state, it follows the resetting procedure outlined in Section 6.5.", path: "", function: "", line: 0, note: "" },
803
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2LINKACTIVE", line: 1281, note: "If SESSACTIVE: InformPartner(NORMALCLOSE) + reset via L2SABM. LinBPQ uses SESSACTIVE flag instead of V(s)==V(a) — no DL-CONNECT-Indication" },
804
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "sabm_e_frame", line: 4442, note: "state_3/state_4 fall-through: UA F=p, clear_exception_conditions, error F, discard queue if vs!=va, reset V(), STOP_T1/START_T3" },
805
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::sabm", line: 1506, note: "Sets version 2, delegates to sabm_or_sabme()" },
806
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 146, note: "Combined SABM/SABME case 146-165. Collapses figure's t40-t43 — no per-F-bit or per-V-state branching" },
807
+ ],
808
+ loops: [],
809
+ },
810
+ {
811
+ id: "t41_sabm_received_vs_eq_va",
812
+ from: "Connected",
813
+ on: "SABM_received",
814
+ guard: "V_s_eq_V_a",
815
+ actions: [
816
+ { verb: "F := P", kind: "processing" },
817
+ { verb: "set_version_2_0", kind: "processing" },
818
+ { verb: "UA", kind: "signal_lower" },
819
+ { verb: "Clear_Exception_Conditions", kind: "subroutine" },
820
+ { verb: "DL_ERROR_indication_F", kind: "signal_upper" },
821
+ { verb: "stop_T1", kind: "processing" },
822
+ { verb: "start_T3", kind: "processing" },
823
+ { verb: "V(a) := 0", kind: "processing" },
824
+ { verb: "V(s) := 0", kind: "processing" },
825
+ { verb: "V(r) := 0", kind: "processing" },
826
+ { verb: "RC := 0", kind: "processing" },
827
+ ],
828
+ next: "Connected",
829
+ notes: "Peer SABM with V(s)==V(a) (no unacked data) — silent reset, no\nDL-CONNECT indication to upper layer.\n",
830
+ references: [
831
+ { source: "spec_prose", cite: "§6.3.3", quote: "If the TNC receives a SABM(E) command while in the information-transfer state, it follows the resetting procedure outlined in Section 6.5.", path: "", function: "", line: 0, note: "" },
832
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2LINKACTIVE", line: 1275, note: "SESSACTIVE==0: repeat UA. Treats SABM as missed-UA case — no DL-CONNECT-Indication. Divergence from spec" },
833
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "sabm_e_frame", line: 4468, note: "vs!=va branch: discard I queue and re-issue server_link_established (figure's DL-CONNECT indication path)" },
834
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::sabm_or_sabme", line: 1433, note: "Author: \"2017 spec says to stop both T1 and T3 in state timer recovery. That can't be right, can it?\" (only T1 stopped)" },
835
+ ],
836
+ loops: [],
837
+ },
838
+ {
839
+ id: "t42_sabme_received_vs_neq_va",
840
+ from: "Connected",
841
+ on: "SABME_received",
842
+ guard: "not V_s_eq_V_a",
843
+ actions: [
844
+ { verb: "F := P", kind: "processing" },
845
+ { verb: "set_version_2_2", kind: "processing" },
846
+ { verb: "UA", kind: "signal_lower" },
847
+ { verb: "Clear_Exception_Conditions", kind: "subroutine" },
848
+ { verb: "DL_ERROR_indication_F", kind: "signal_upper" },
849
+ { verb: "discard_I_frame_queue", kind: "processing" },
850
+ { verb: "DL_CONNECT_indication", kind: "signal_upper" },
851
+ { verb: "stop_T1", kind: "processing" },
852
+ { verb: "start_T3", kind: "processing" },
853
+ { verb: "V(a) := 0", kind: "processing" },
854
+ { verb: "V(s) := 0", kind: "processing" },
855
+ { verb: "V(r) := 0", kind: "processing" },
856
+ { verb: "RC := 0", kind: "processing" },
857
+ ],
858
+ next: "Connected",
859
+ notes: "",
860
+ references: [
861
+ { source: "spec_prose", cite: "§6.3.3", quote: "If the TNC receives a SABM(E) command while in the information-transfer state, it follows the resetting procedure outlined in Section 6.5.", path: "", function: "", line: 0, note: "" },
862
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "sabm_e_frame", line: 4454, note: "SABM and SABME merge in state_3. extended bool only routes set_version — structural collapse vs figc4.4" },
863
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::sabme", line: 1515, note: "Sets version 2.2, delegates to sabm_or_sabme()" },
864
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 147, note: "Same combined case as t40. Extended modulus set at 152-153" },
865
+ ],
866
+ loops: [],
867
+ },
868
+ {
869
+ id: "t43_sabme_received_vs_eq_va",
870
+ from: "Connected",
871
+ on: "SABME_received",
872
+ guard: "V_s_eq_V_a",
873
+ actions: [
874
+ { verb: "F := P", kind: "processing" },
875
+ { verb: "set_version_2_2", kind: "processing" },
876
+ { verb: "UA", kind: "signal_lower" },
877
+ { verb: "Clear_Exception_Conditions", kind: "subroutine" },
878
+ { verb: "DL_ERROR_indication_F", kind: "signal_upper" },
879
+ { verb: "stop_T1", kind: "processing" },
880
+ { verb: "start_T3", kind: "processing" },
881
+ { verb: "V(a) := 0", kind: "processing" },
882
+ { verb: "V(s) := 0", kind: "processing" },
883
+ { verb: "V(r) := 0", kind: "processing" },
884
+ { verb: "RC := 0", kind: "processing" },
885
+ ],
886
+ next: "Connected",
887
+ notes: "",
888
+ references: [
889
+ { source: "spec_prose", cite: "§6.3.3", quote: "If the TNC receives a SABM(E) command while in the information-transfer state, it follows the resetting procedure outlined in Section 6.5.", path: "", function: "", line: 0, note: "" },
890
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "sabm_e_frame", line: 4468, note: "same as t41 — direwolf doesn't distinguish SABM/SABME branches" },
891
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::sabm_or_sabme", line: 1447, note: "Author: \"Added in 2017 spec, but only for Connected. TODO: should this be set also for TimerRecovery?\" on rc=0 reset" },
892
+ ],
893
+ loops: [],
894
+ },
895
+ {
896
+ id: "t44_frmr_received_v22",
897
+ from: "Connected",
898
+ on: "FRMR_received",
899
+ guard: "version_2_2",
900
+ actions: [
901
+ { verb: "DL_ERROR_indication_K", kind: "signal_upper" },
902
+ { verb: "Establish_Data_Link", kind: "subroutine" },
903
+ { verb: "clear_layer_3_initiated", kind: "processing" },
904
+ ],
905
+ next: "AwaitingConnection22",
906
+ notes: "",
907
+ references: [
908
+ { source: "spec_prose", cite: "§6.5 ¶2", quote: "A TNC initiates a reset procedure whenever it receives an unexpected UA response frame, or after receipt of a FRMR frame from a TNC using an older version of the protocol.", path: "", function: "", line: 0, note: "" },
909
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2_PROCESS", line: 2168, note: "FRMRHACK (L2Code.c:24) treats FRMR as DM — InformPartner(LINKLOST) + DM + CLEAROUTLINK. NOTABLE DIVERGENCE: spec says DL-ERROR(K)+Establish. Spec-compliant path commented out at 2192-2202" },
910
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "frmr_frame", line: 5020, note: "state_3/state_4: error K, set_version_2_0 (Erratum 5028: \"FRMR can only be sent by v2.0\"), establish_data_link, → state_1" },
911
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::frmr", line: 1760, note: "Both Connected and TimerRecovery: layer3_initiated=false, DlError::K, establish_data_link, → AwaitingConnection" },
912
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 248, note: "AX25_FRMR (and AX25_ILLEGAL) → establish_data_link + STATE_1. No DL-ERROR(K) report; no FRMR info bytes inspected" },
913
+ ],
914
+ loops: [],
915
+ },
916
+ {
917
+ id: "t45_frmr_received_v20",
918
+ from: "Connected",
919
+ on: "FRMR_received",
920
+ guard: "not version_2_2",
921
+ actions: [
922
+ { verb: "DL_ERROR_indication_K", kind: "signal_upper" },
923
+ { verb: "Establish_Data_Link", kind: "subroutine" },
924
+ { verb: "clear_layer_3_initiated", kind: "processing" },
925
+ ],
926
+ next: "AwaitingConnection",
927
+ notes: "",
928
+ references: [
929
+ { source: "spec_prose", cite: "§6.5 ¶2", quote: "A TNC initiates a reset procedure whenever it receives an unexpected UA response frame, or after receipt of a FRMR frame from a TNC using an older version of the protocol.", path: "", function: "", line: 0, note: "" },
930
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "frmr_frame", line: 5031, note: "single path — direwolf has no separate L3-initiated branch" },
931
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::frmr", line: 1760, note: "same handler as t44" },
932
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2_PROCESS", line: 2168, note: "same FRMRHACK path as t44 — no v22/v20 split" },
933
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 248, note: "same case as t44" },
934
+ ],
935
+ loops: [],
936
+ },
937
+ {
938
+ id: "t46_ua_received_v22",
939
+ from: "Connected",
940
+ on: "UA_received",
941
+ guard: "version_2_2",
942
+ actions: [
943
+ { verb: "DL_ERROR_indication_K", kind: "signal_upper" },
944
+ { verb: "Establish_Data_Link", kind: "subroutine" },
945
+ { verb: "clear_layer_3_initiated", kind: "processing" },
946
+ ],
947
+ next: "AwaitingConnection22",
948
+ notes: "",
949
+ references: [
950
+ { source: "spec_prose", cite: "§6.5 ¶2", quote: "A TNC initiates a reset procedure whenever it receives an unexpected UA response frame, or after receipt of a FRMR frame from a TNC using an older version of the protocol.", path: "", function: "", line: 0, note: "" },
951
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "ua_frame", line: 4948, note: "state_3/state_4: error C (\"Unexpected UA\"), establish_data_link, → state_1 or 5 (modulo author enhancement). Direwolf does NOT branch on F bit — figure's two-path F=1/F=0 collapsed" },
952
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::ua", line: 1748, note: "Author: \"2017 spec says DlError::K, which is undocumented.\" Uses DlError::C, establish_data_link, → AwaitingConnection" },
953
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2_PROCESS", line: 2140, note: "UA in state 5 falls to default → ReleaseBuffer (silent ignore). NOTABLE DIVERGENCE from spec DL-ERROR(C)+Establish" },
954
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 254, note: "No AX25_UA case in STATE_3 switch → silent default. DIVERGENCE: figure t46 calls for DL-ERROR(C)" },
955
+ ],
956
+ loops: [],
957
+ },
958
+ {
959
+ id: "t47_ua_received_v20",
960
+ from: "Connected",
961
+ on: "UA_received",
962
+ guard: "not version_2_2",
963
+ actions: [
964
+ { verb: "DL_ERROR_indication_K", kind: "signal_upper" },
965
+ { verb: "Establish_Data_Link", kind: "subroutine" },
966
+ { verb: "clear_layer_3_initiated", kind: "processing" },
967
+ ],
968
+ next: "AwaitingConnection",
969
+ notes: "",
970
+ references: [
971
+ { source: "spec_prose", cite: "§6.5 ¶2", quote: "A TNC initiates a reset procedure whenever it receives an unexpected UA response frame, or after receipt of a FRMR frame from a TNC using an older version of the protocol.", path: "", function: "", line: 0, note: "" },
972
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "ua_frame", line: 4948, note: "same path as t46 — no F-bit branching" },
973
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::ua", line: 1748, note: "same handler as t46" },
974
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2_PROCESS", line: 2140, note: "same silent-ignore as t46" },
975
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 254, note: "same default-case drop as t46. Linux uniform: no F-bit distinction" },
976
+ ],
977
+ loops: [],
978
+ },
979
+ {
980
+ id: "t48_ui_received_p_eq_0",
981
+ from: "Connected",
982
+ on: "UI_received",
983
+ guard: "not P_eq_1",
984
+ actions: [
985
+ { verb: "UI_Check", kind: "subroutine" },
986
+ ],
987
+ next: "Connected",
988
+ notes: "",
989
+ references: [
990
+ { source: "spec_prose", cite: "§6.3.7", quote: "AX.25 uses a special frame for this operation, the Unnumbered Information (UI) frame…Since this mode is connectionless, there are no requests for retransmissions of bad frames.", path: "", function: "", line: 0, note: "" },
991
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "ui_frame", line: 5127, note: "Only runs when cmd && pf==1. state_3/4 case calls enquiry_response with frame_type_U_UI" },
992
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::ui", line: 1770, note: "Calls Data::ui_check(cr, len); no enquiry response unless poll set" },
993
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2LINKACTIVE", line: 1065, note: "UI dispatched by PID (Netrom/IP/ARP). No UI_Check DL-error semantics — LinBPQ just routes" },
994
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_in.c", function: "ax25_rcv", line: 229, note: "UI intercepted before LAPB state machine — handled state-independently" },
995
+ ],
996
+ loops: [],
997
+ },
998
+ {
999
+ id: "t49_ui_received_p_eq_1",
1000
+ from: "Connected",
1001
+ on: "UI_received",
1002
+ guard: "P_eq_1",
1003
+ actions: [
1004
+ { verb: "UI_Check", kind: "subroutine" },
1005
+ { verb: "Enquiry_Response_F_1", kind: "subroutine" },
1006
+ ],
1007
+ next: "Connected",
1008
+ notes: "",
1009
+ references: [
1010
+ { source: "spec_prose", cite: "§6.3.7", quote: "AX.25 uses a special frame for this operation, the Unnumbered Information (UI) frame…", path: "", function: "", line: 0, note: "" },
1011
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::ui", line: 1772, note: "Poll branch: act.push(data.enquiry_response(true))" },
1012
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "ui_frame", line: 5127, note: "same path as t48 — direwolf only handles cmd && pf==1 case" },
1013
+ ],
1014
+ loops: [],
1015
+ },
1016
+ {
1017
+ id: "t50_disc_received",
1018
+ from: "Connected",
1019
+ on: "DISC_received",
1020
+ guard: "",
1021
+ actions: [
1022
+ { verb: "discard_I_frame_queue", kind: "processing" },
1023
+ { verb: "F := P", kind: "processing" },
1024
+ { verb: "UA", kind: "signal_lower" },
1025
+ { verb: "DL_DISCONNECT_indication", kind: "signal_upper" },
1026
+ { verb: "stop_T3", kind: "processing" },
1027
+ { verb: "start_T1", kind: "processing" },
1028
+ ],
1029
+ next: "Disconnected",
1030
+ notes: "Peer wants to disconnect — confirm with UA, notify L3, swap\ntimers and head to Disconnected. The figure draws stop_T3 then\nstart_T1 (and indeed wait for the peer's ack of the UA via T1).\n",
1031
+ references: [
1032
+ { source: "spec_prose", cite: "§6.3.4 ¶2", quote: "After receiving a valid DISC command, the TNC sends a UA response frame and enters the disconnected state.", path: "", function: "", line: 0, note: "" },
1033
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2LINKACTIVE", line: 1090, note: "InformPartner(NORMALCLOSE) + L2SENDUA + CLEAROUTLINK. Goes straight to Disconnected — no explicit T3-stop/T1-start" },
1034
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "disc_frame", line: 4557, note: "state_3/state_4: discard queue, UA F=p, server_link_terminated, STOP_T1, STOP_T3, → state_0" },
1035
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::disc", line: 1537, note: "Author marks \"Done.\" Clear queue, stop T1/T3, SendUa + EOF + Disconnected" },
1036
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 167, note: "UA F=pf, then ax25_disconnect(0) → STATE_0" },
1037
+ ],
1038
+ loops: [],
1039
+ },
1040
+ {
1041
+ id: "t51_dm_received",
1042
+ from: "Connected",
1043
+ on: "DM_received",
1044
+ guard: "",
1045
+ actions: [
1046
+ { verb: "DL_ERROR_indication_E", kind: "signal_upper" },
1047
+ { verb: "DL_DISCONNECT_indication", kind: "signal_upper" },
1048
+ { verb: "discard_I_frame_queue", kind: "processing" },
1049
+ { verb: "stop_T1", kind: "processing" },
1050
+ { verb: "stop_T3", kind: "processing" },
1051
+ ],
1052
+ next: "Disconnected",
1053
+ notes: "Unexpected DM while connected — peer claims it's not connected.\nDL-ERROR(E), tell L3 the link is gone, stop both timers.\n",
1054
+ references: [
1055
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2_PROCESS", line: 2145, note: "InformPartner(LINKLOST) + CLEAROUTLINK. No explicit DL-ERROR(E)" },
1056
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "dm_frame", line: 4696, note: "state_3/state_4: error E, server_link_terminated, → state_0. Direwolf does NOT branch on F bit (spec figure shows F=1/F=0 paths)" },
1057
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::dm", line: 1523, note: "Author marks \"Done.\" DL-DISCONNECT, clear queue, stop T1/T3, DlError::E, → Disconnected" },
1058
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 172, note: "ax25_disconnect(ECONNRESET) → STATE_0. Linux ignores F bit (figure t51 only acts on F=1)" },
1059
+ ],
1060
+ loops: [],
1061
+ },
1062
+ {
1063
+ id: "t52_rr_received_nr_in_window",
1064
+ from: "Connected",
1065
+ on: "RR_received",
1066
+ guard: "V_a_le_N_r_le_V_s",
1067
+ actions: [
1068
+ { verb: "clear_peer_receiver_busy", kind: "processing" },
1069
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1070
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
1071
+ ],
1072
+ next: "Connected",
1073
+ notes: "",
1074
+ references: [
1075
+ { source: "spec_prose", cite: "§6.4.6", quote: "Whenever an I or S frame is correctly received, even in a busy condition, the N(R) of the received frame is checked to see if it includes an acknowledgement of outstanding sent I frames.", path: "", function: "", line: 0, note: "" },
1076
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2403, note: "treatasRR: clears RNRSET; ACKMSG releases acked frames. Stays in 5" },
1077
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "rr_rnr_frame", line: 3545, note: "state_3 entry. peer_receiver_busy=!ready. Erratum 3549-3552: check_need_for_response only for cmd&&pf (figure has unconditional)" },
1078
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::rr_connected", line: 1361, note: "Connected branch. Clears peer_receiver_busy, check_need_for_response, check_iframe_acked" },
1079
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 182, note: "Combined RR/RNR case. enquiry_response on cmd&&pf, then check_iframes_acked" },
1080
+ ],
1081
+ loops: [],
1082
+ },
1083
+ {
1084
+ id: "t53_rr_received_nr_out_of_window_v22",
1085
+ from: "Connected",
1086
+ on: "RR_received",
1087
+ guard: "not V_a_le_N_r_le_V_s and version_2_2",
1088
+ actions: [
1089
+ { verb: "clear_peer_receiver_busy", kind: "processing" },
1090
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1091
+ { verb: "N_r_Error_Recovery", kind: "subroutine" },
1092
+ ],
1093
+ next: "AwaitingConnection22",
1094
+ notes: "",
1095
+ references: [
1096
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2277, note: "SDREJF set by SDNRCHK → SDFRMR (FRMR state). NOTABLE DIVERGENCE: spec says N(r) Error Recovery → AwaitingConnection22. LinBPQ goes to FRMR state 3" },
1097
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "rr_rnr_frame", line: 3569, note: "bad N(R): nr_error_recovery, → state_1 (or state_5 modulo author enhancement)" },
1098
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::rr_connected", line: 1366, note: "N(R) out of window: nr_error_recovery → AwaitingConnection" },
1099
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 184, note: "On invalid N(R): nr_error_recovery + STATE_1" },
1100
+ ],
1101
+ loops: [],
1102
+ },
1103
+ {
1104
+ id: "t54_rr_received_nr_out_of_window_v20",
1105
+ from: "Connected",
1106
+ on: "RR_received",
1107
+ guard: "not V_a_le_N_r_le_V_s and not version_2_2",
1108
+ actions: [
1109
+ { verb: "clear_peer_receiver_busy", kind: "processing" },
1110
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1111
+ { verb: "N_r_Error_Recovery", kind: "subroutine" },
1112
+ ],
1113
+ next: "AwaitingConnection",
1114
+ notes: "",
1115
+ references: [
1116
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2277, note: "same SDFRMR path as t53 — no v22/v20 split" },
1117
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "rr_rnr_frame", line: 3569, note: "same as t53" },
1118
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::rr_connected", line: 1366, note: "same as t53" },
1119
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 184, note: "same as t53" },
1120
+ ],
1121
+ loops: [],
1122
+ },
1123
+ {
1124
+ id: "t55_rnr_received_nr_in_window",
1125
+ from: "Connected",
1126
+ on: "RNR_received",
1127
+ guard: "V_a_le_N_r_le_V_s",
1128
+ actions: [
1129
+ { verb: "set_peer_receiver_busy", kind: "processing" },
1130
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1131
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
1132
+ ],
1133
+ next: "Connected",
1134
+ notes: "",
1135
+ references: [
1136
+ { source: "spec_prose", cite: "§6.4.9", quote: "Whenever a TNC receives an RNR frame, it stops transmitting I frames until the busy condition is cleared.", path: "", function: "", line: 0, note: "" },
1137
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2410, note: "Sets RNRSET (peer busy). RNR(F) restarts L2TIMER (2511) — explicit divergence comment at 2507" },
1138
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "rr_rnr_frame", line: 3547, note: "Same code path as RR; ready=0 sets peer_receiver_busy=1. Structural collapse RR/RNR" },
1139
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 181, note: "Shares RR/RNR case. Sets PEER_RX_BUSY (only difference from RR)" },
1140
+ ],
1141
+ loops: [],
1142
+ },
1143
+ {
1144
+ id: "t56_rnr_received_nr_out_of_window_v22",
1145
+ from: "Connected",
1146
+ on: "RNR_received",
1147
+ guard: "not V_a_le_N_r_le_V_s and version_2_2",
1148
+ actions: [
1149
+ { verb: "set_peer_receiver_busy", kind: "processing" },
1150
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1151
+ { verb: "N_r_Error_Recovery", kind: "subroutine" },
1152
+ ],
1153
+ next: "AwaitingConnection22",
1154
+ notes: "",
1155
+ references: [
1156
+ { source: "spec_prose", cite: "§6.4.9", quote: "Whenever a TNC receives an RNR frame, it stops transmitting I frames until the busy condition is cleared.", path: "", function: "", line: 0, note: "" },
1157
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2277, note: "same SDFRMR path as t53" },
1158
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "rr_rnr_frame", line: 3569, note: "RR/RNR converge for bad-N(R)" },
1159
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 184, note: "same as t53" },
1160
+ ],
1161
+ loops: [],
1162
+ },
1163
+ {
1164
+ id: "t57_rnr_received_nr_out_of_window_v20",
1165
+ from: "Connected",
1166
+ on: "RNR_received",
1167
+ guard: "not V_a_le_N_r_le_V_s and not version_2_2",
1168
+ actions: [
1169
+ { verb: "set_peer_receiver_busy", kind: "processing" },
1170
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1171
+ { verb: "N_r_Error_Recovery", kind: "subroutine" },
1172
+ ],
1173
+ next: "AwaitingConnection",
1174
+ notes: "",
1175
+ references: [
1176
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2277, note: "same SDFRMR path" },
1177
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "rr_rnr_frame", line: 3569, note: "same as t56" },
1178
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 184, note: "same as t53" },
1179
+ ],
1180
+ loops: [],
1181
+ },
1182
+ {
1183
+ id: "t58_lm_seize_confirm_no_ack_pending",
1184
+ from: "Connected",
1185
+ on: "LM_SEIZE_confirm",
1186
+ guard: "not acknowledge_pending",
1187
+ actions: [
1188
+ { verb: "LM_release_request", kind: "signal_lower" },
1189
+ ],
1190
+ next: "Connected",
1191
+ notes: "",
1192
+ references: [
1193
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "lm_seize_confirm", line: 2223, note: "state_3/4: i_frame_pop_off_queue, no enquiry_response (acknowledge_pending false). Erratum 2244: states 3 & 4 merged per 2006 revision" },
1194
+ ],
1195
+ loops: [],
1196
+ },
1197
+ {
1198
+ id: "t59_lm_seize_confirm_ack_pending",
1199
+ from: "Connected",
1200
+ on: "LM_SEIZE_confirm",
1201
+ guard: "acknowledge_pending",
1202
+ actions: [
1203
+ { verb: "clear_acknowledge_pending", kind: "processing" },
1204
+ { verb: "Enquiry_Response_F_0", kind: "subroutine" },
1205
+ { verb: "LM_release_request", kind: "signal_lower" },
1206
+ ],
1207
+ next: "Connected",
1208
+ notes: "",
1209
+ references: [
1210
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "lm_seize_confirm", line: 2231, note: "Same code path; acknowledge_pending check at 2235" },
1211
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "L2TimerProc", line: 3526, note: "Closest analog: delayed-ack timer (L2ACKREQ) fires SEND_RR_RESP. Not literally LM-SEIZE — deferred ack mechanism" },
1212
+ ],
1213
+ loops: [],
1214
+ },
1215
+ {
1216
+ id: "t60_srej_received_nr_in_window_pf_eq_0",
1217
+ from: "Connected",
1218
+ on: "SREJ_received",
1219
+ guard: "V_a_le_N_r_le_V_s and not P_or_F_eq_1",
1220
+ actions: [
1221
+ { verb: "clear_peer_receiver_busy", kind: "processing" },
1222
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1223
+ { verb: "stop_T1", kind: "processing" },
1224
+ { verb: "start_T3", kind: "processing" },
1225
+ { verb: "Select_T1_Value", kind: "subroutine" },
1226
+ { verb: "push_old_I_frame_N_r_on_queue", kind: "internal_out" },
1227
+ { verb: "LM_data_request", kind: "signal_lower" },
1228
+ { verb: "stop_T3", kind: "processing" },
1229
+ { verb: "start_T1", kind: "processing" },
1230
+ { verb: "clear_acknowledge_pending", kind: "processing" },
1231
+ ],
1232
+ next: "Connected",
1233
+ notes: "",
1234
+ references: [
1235
+ { source: "spec_prose", cite: "§6.4.8", quote: "After receiving a SREJ frame, the transmitting TNC retransmits the individual I frame indicated by the N(R) contained in the SREJ at the next available opportunity.", path: "", function: "", line: 0, note: "" },
1236
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2954, note: "SDNRCHK: SREJ without F doesn't ACK (early return)" },
1237
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "srej_frame", line: 4057, note: "state_3 entry. Erratum 4061-4067: figure has check_need_for_response but SREJ can only be response, so removed" },
1238
+ ],
1239
+ loops: [],
1240
+ },
1241
+ {
1242
+ id: "t61_srej_received_nr_in_window_pf_eq_1",
1243
+ from: "Connected",
1244
+ on: "SREJ_received",
1245
+ guard: "V_a_le_N_r_le_V_s and P_or_F_eq_1",
1246
+ actions: [
1247
+ { verb: "clear_peer_receiver_busy", kind: "processing" },
1248
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1249
+ { verb: "V(a) := N(r)", kind: "processing" },
1250
+ { verb: "stop_T1", kind: "processing" },
1251
+ { verb: "start_T3", kind: "processing" },
1252
+ { verb: "Select_T1_Value", kind: "subroutine" },
1253
+ { verb: "push_old_I_frame_N_r_on_queue", kind: "internal_out" },
1254
+ { verb: "LM_data_request", kind: "signal_lower" },
1255
+ { verb: "stop_T3", kind: "processing" },
1256
+ { verb: "start_T1", kind: "processing" },
1257
+ { verb: "clear_acknowledge_pending", kind: "processing" },
1258
+ ],
1259
+ next: "Connected",
1260
+ notes: "",
1261
+ references: [
1262
+ { source: "spec_prose", cite: "§6.4.8", quote: "If the P bit was set, then all frames up to N(R)-1 are acknowledged.", path: "", function: "", line: 0, note: "" },
1263
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2303, note: "SREJ(F) resend: clear POLLSENT, look up FRAMES[NS], resend. No V(s) advance / T3 restart — collapses spec's t60/t61" },
1264
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "srej_frame", line: 4079, note: "resend_for_srej. Author addition at 4082-4093: STOP_T3/START_T1 after sending I frame — avoids mutual-timeout deadlock" },
1265
+ ],
1266
+ loops: [],
1267
+ },
1268
+ {
1269
+ id: "t62_srej_received_nr_out_of_window_v22",
1270
+ from: "Connected",
1271
+ on: "SREJ_received",
1272
+ guard: "not V_a_le_N_r_le_V_s and version_2_2",
1273
+ actions: [
1274
+ { verb: "clear_peer_receiver_busy", kind: "processing" },
1275
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1276
+ { verb: "N_r_Error_Recovery", kind: "subroutine" },
1277
+ ],
1278
+ next: "AwaitingConnection22",
1279
+ notes: "",
1280
+ references: [
1281
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2277, note: "same SDFRMR path as t53" },
1282
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "srej_frame", line: 4096, note: "bad N(R): nr_error_recovery, → state_1 or 5. Erratum 4098: \"Flow chart shows state 1 but that would not be appropriate if modulo is 128\"" },
1283
+ ],
1284
+ loops: [],
1285
+ },
1286
+ {
1287
+ id: "t63_srej_received_nr_out_of_window_v20",
1288
+ from: "Connected",
1289
+ on: "SREJ_received",
1290
+ guard: "not V_a_le_N_r_le_V_s and not version_2_2",
1291
+ actions: [
1292
+ { verb: "clear_peer_receiver_busy", kind: "processing" },
1293
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1294
+ { verb: "N_r_Error_Recovery", kind: "subroutine" },
1295
+ ],
1296
+ next: "AwaitingConnection",
1297
+ notes: "",
1298
+ references: [
1299
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2277, note: "same as t62" },
1300
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "srej_frame", line: 4096, note: "same as t62" },
1301
+ ],
1302
+ loops: [],
1303
+ },
1304
+ {
1305
+ id: "t64_rej_received_nr_in_window",
1306
+ from: "Connected",
1307
+ on: "REJ_received",
1308
+ guard: "V_a_le_N_r_le_V_s",
1309
+ actions: [
1310
+ { verb: "clear_peer_receiver_busy", kind: "processing" },
1311
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1312
+ { verb: "V(a) := N(r)", kind: "processing" },
1313
+ { verb: "start_T1", kind: "processing" },
1314
+ { verb: "stop_T3", kind: "processing" },
1315
+ { verb: "clear_acknowledge_pending", kind: "processing" },
1316
+ { verb: "Select_T1_Value", kind: "subroutine" },
1317
+ { verb: "Invoke_Retransmission", kind: "subroutine" },
1318
+ ],
1319
+ next: "Connected",
1320
+ notes: "",
1321
+ references: [
1322
+ { source: "spec_prose", cite: "§6.4.7", quote: "After receiving a REJ frame, the transmitting TNC sets its send state variable to the same value as the REJ frame's received sequence number in the control field. The TNC then retransmits any I frame(s) outstanding at the next available opportunity.", path: "", function: "", line: 0, note: "" },
1323
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2481, note: "REJ → POLLSENT check → RESETNS resets V(s) to N(r) (= Invoke_Retransmission). T1 cleared at 2486. No explicit Select_T1_Value" },
1324
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "rej_frame", line: 3804, note: "state_3: peer_receiver_busy=0, check_need_for_response UNCONDITIONAL (different from rr_rnr_frame), is_good_nr" },
1325
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 192, note: "AX25_REJ: clears PEER_RX_BUSY, enquiry_response on cmd&&pf, ax25_frames_acked + RTT + stop T1 + start T3 + requeue_frames on valid N(R)" },
1326
+ ],
1327
+ loops: [],
1328
+ },
1329
+ {
1330
+ id: "t65_rej_received_nr_out_of_window_v22",
1331
+ from: "Connected",
1332
+ on: "REJ_received",
1333
+ guard: "not V_a_le_N_r_le_V_s and version_2_2",
1334
+ actions: [
1335
+ { verb: "clear_peer_receiver_busy", kind: "processing" },
1336
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1337
+ { verb: "N_r_Error_Recovery", kind: "subroutine" },
1338
+ ],
1339
+ next: "AwaitingConnection22",
1340
+ notes: "",
1341
+ references: [
1342
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2277, note: "same SDFRMR path" },
1343
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "rej_frame", line: 3840, note: "bad N(R): nr_error_recovery, → state_1 or 5 (author enhancement)" },
1344
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 194, note: "same as t53" },
1345
+ ],
1346
+ loops: [],
1347
+ },
1348
+ {
1349
+ id: "t66_rej_received_nr_out_of_window_v20",
1350
+ from: "Connected",
1351
+ on: "REJ_received",
1352
+ guard: "not V_a_le_N_r_le_V_s and not version_2_2",
1353
+ actions: [
1354
+ { verb: "clear_peer_receiver_busy", kind: "processing" },
1355
+ { verb: "Check_Need_For_Response", kind: "subroutine" },
1356
+ { verb: "N_r_Error_Recovery", kind: "subroutine" },
1357
+ ],
1358
+ next: "AwaitingConnection",
1359
+ notes: "",
1360
+ references: [
1361
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SFRAME", line: 2277, note: "same as t65" },
1362
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "rej_frame", line: 3840, note: "same as t65" },
1363
+ { source: "linux_oot", cite: "", quote: "", path: "net/ax25/ax25_std_in.c", function: "ax25_std_state3_machine", line: 194, note: "same as t65" },
1364
+ ],
1365
+ loops: [],
1366
+ },
1367
+ {
1368
+ id: "t67_i_received_in_seq_stored_p_eq_1",
1369
+ from: "Connected",
1370
+ on: "I_received",
1371
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and N_s_eq_V_r and V_r_I_frame_stored and P_eq_1",
1372
+ actions: [
1373
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
1374
+ { verb: "V(r) := V(r) + 1", kind: "processing" },
1375
+ { verb: "clear_reject_exception", kind: "processing" },
1376
+ { verb: "decrement_srej_exception_if_gt_0", kind: "processing" },
1377
+ { verb: "DL_DATA_indication", kind: "signal_upper" },
1378
+ { verb: "retrieve_stored_V_r_I_frame", kind: "processing" },
1379
+ { verb: "DL_DATA_indication", kind: "signal_upper" },
1380
+ { verb: "V(r) := V(r) + 1", kind: "processing" },
1381
+ { verb: "F := 1", kind: "processing" },
1382
+ { verb: "N(r) := V(r)", kind: "processing" },
1383
+ { verb: "RR", kind: "signal_lower" },
1384
+ { verb: "clear_acknowledge_pending", kind: "processing" },
1385
+ ],
1386
+ next: "Connected",
1387
+ notes: "Loop body (retrieve / DL-DATA Indication / V(r)++) drained\nvia the `loop_while` step — iterates until V(r) I Frame Stored\nreturns false. Compiled to flat Actions[] with a LoopRange entry;\nruntime loop semantics handled by loop-aware dispatchers, falls\nback to one-iteration on legacy consumers (matches today's smoke\ntest behaviour).\n",
1388
+ references: [
1389
+ { source: "spec_prose", cite: "§6.4.4.2", quote: "When an I frame is received…the frame is retained…This mode requires frame queuing and frame resequencing at the receiver.", path: "", function: "", line: 0, note: "" },
1390
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1628, note: "Loop to drain stored out-of-order iframes — stub only (`while false`); TODO: \"check for stored out of order frames\"" },
1391
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 2906, note: "Drain SREJ-buffered out-of-order frames now contiguous with V(R) — equivalent of figure's stored-frame loop" },
1392
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDIFRM", line: 2586, note: "After REJ, when missing frame arrives, replays stored RXFRAMES via CheckNSLoop → PROC_I_FRAME at 2593" },
1393
+ ],
1394
+ loops: [
1395
+ { start: 5, length: 3, predicate: "V_r_I_frame_stored" },
1396
+ ],
1397
+ },
1398
+ {
1399
+ id: "t68_i_received_in_seq_stored_p_eq_0_ack_pending",
1400
+ from: "Connected",
1401
+ on: "I_received",
1402
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and N_s_eq_V_r and V_r_I_frame_stored and not P_eq_1 and acknowledge_pending",
1403
+ actions: [
1404
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
1405
+ { verb: "V(r) := V(r) + 1", kind: "processing" },
1406
+ { verb: "clear_reject_exception", kind: "processing" },
1407
+ { verb: "decrement_srej_exception_if_gt_0", kind: "processing" },
1408
+ { verb: "DL_DATA_indication", kind: "signal_upper" },
1409
+ { verb: "retrieve_stored_V_r_I_frame", kind: "processing" },
1410
+ { verb: "DL_DATA_indication", kind: "signal_upper" },
1411
+ { verb: "V(r) := V(r) + 1", kind: "processing" },
1412
+ ],
1413
+ next: "Connected",
1414
+ notes: "Same stored-frame loop as t67; continues into the not-polled /\nack-pending tail.\n",
1415
+ references: [
1416
+ { source: "spec_prose", cite: "§6.4.4.2", quote: "When an I frame is received…the frame is retained…This mode requires frame queuing and frame resequencing at the receiver.", path: "", function: "", line: 0, note: "" },
1417
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1628, note: "same loop stub as t67" },
1418
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 2906, note: "same drain loop as t67" },
1419
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "SDIFRM", line: 2586, note: "same replay path as t67" },
1420
+ ],
1421
+ loops: [
1422
+ { start: 5, length: 3, predicate: "V_r_I_frame_stored" },
1423
+ ],
1424
+ },
1425
+ {
1426
+ id: "t69_i_received_in_seq_stored_p_eq_0_no_ack_pending",
1427
+ from: "Connected",
1428
+ on: "I_received",
1429
+ guard: "command and info_field_valid and V_a_le_N_r_le_V_s and not own_receiver_busy and N_s_eq_V_r and V_r_I_frame_stored and not P_eq_1 and not acknowledge_pending",
1430
+ actions: [
1431
+ { verb: "Check_I_Frame_Acknowledged", kind: "subroutine" },
1432
+ { verb: "V(r) := V(r) + 1", kind: "processing" },
1433
+ { verb: "clear_reject_exception", kind: "processing" },
1434
+ { verb: "decrement_srej_exception_if_gt_0", kind: "processing" },
1435
+ { verb: "DL_DATA_indication", kind: "signal_upper" },
1436
+ { verb: "retrieve_stored_V_r_I_frame", kind: "processing" },
1437
+ { verb: "DL_DATA_indication", kind: "signal_upper" },
1438
+ { verb: "V(r) := V(r) + 1", kind: "processing" },
1439
+ { verb: "LM_seize_request", kind: "signal_lower" },
1440
+ { verb: "set_acknowledge_pending", kind: "processing" },
1441
+ ],
1442
+ next: "Connected",
1443
+ notes: "Same stored-frame loop as t67; continues into the not-polled /\nno-ack tail.\n",
1444
+ references: [
1445
+ { source: "spec_prose", cite: "§6.4.4.2", quote: "When an I frame is received…the frame is retained…This mode requires frame queuing and frame resequencing at the receiver.", path: "", function: "", line: 0, note: "" },
1446
+ { source: "rax25", cite: "", quote: "", path: "src/state.rs", function: "Connected::iframe", line: 1628, note: "same loop stub" },
1447
+ { source: "direwolf", cite: "", quote: "", path: "src/ax25_link.c", function: "i_frame_continued", line: 2906, note: "same drain loop" },
1448
+ { source: "linbpq", cite: "", quote: "", path: "L2Code.c", function: "RR_OR_RNR", line: 4185, note: "Second replay loop in RR_OR_RNR — replays RXFRAMES when about to send supervisory response" },
1449
+ ],
1450
+ loops: [
1451
+ { start: 5, length: 3, predicate: "V_r_I_frame_stored" },
1452
+ ],
1453
+ },
1454
+ ],
1455
+ };
1456
+ //# sourceMappingURL=connected.g.js.map