1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
# TNC / Modem bring-up · MAX25-Stack
English bring-up for **TNCs** (HDLC + KISS on host serial) and **BayCom/based** modems (bits↔AFSK + PTT; host owns HDLC). Applies to MAX25-Stack and to any stack that attaches the same hardware classes.
---
## 1. Roles — TNC vs modem
| Class | What it is | Host owns | Typical face |
|-------|------------|-----------|--------------|
| **TNC** (TNC2C, PK-TNC2, TNC2multi, …) | Firmware does HDLC; host talks **command mode** then **KISS** on UART | Serial lifecycle, recovery, KISS entry | `tnc2c`, `pktnc2`, … |
| **BayCom/based** modem | Board is **modem-only** (TCM3105-class AFSK + PTT) | HDLC, SER12 bit clock, KISS PTY, PTT | **`max25e0`** (bcpr) |
| Soft-modem (CRDOP) | Soundcard AFSK | Audio + soft HDLC | `soft-crdop` |
```text
TNC path: Radio AF ↔ TNC (HDLC+modem) ↔ UART KISS ↔ host stack
Modem path: Radio AF ↔ BayCom/based chip ↔ SER12 UART ↔ host HDLC (bcpr) ↔ KISS
```
Do **not** treat a BayCom/based board as a TNC. Do **not** open the same `/dev/tty*` from two processes.
---
## 2. KISS bring-up (any stack)
### 2.1 Serial ownership
| Rule | Why |
|------|-----|
| **One opener** per UART | Dual openers → garbled RX, failed recovery, flaky KISS |
| Hold **DTR+RTS high** while the port is owned | Landolt TNC2C terminal detect needs DTR high at power-on; drop on close → echo-only |
| Match **baud / line** to hardware | Wrong baud → garbage or echo only |
| Device class (typical) | Baud | Line | Notes |
|------------------------|------|------|-------|
| Landolt TNC2C | **19200** | 8N1 | Not 4800/9600/7E1 |
| PK-TNC2 / many TF boards | **9600** | 8N1 | Profile-dependent |
| BayCom/based SER12 | UART clocked by host SER12 | — | Product path: **max25-bcpr**, not kernel `baycom_ser_fdx` |
### 2.2 Command sequence (TheFirmware / TNC-2 class)
| Step | Action |
|------|--------|
| 1 | Free the port (`pkill` stray terminals; one stack owns the fd) |
| 2 | Open UART with DTR+RTS high; settle ~2 s |
| 3 | Reach **terminal / host command mode** (banner / `cmd:`) — see recovery below |
| 4 | Set callsign / radio params as required by firmware |
| 5 | Enter KISS: TheFirmware **ESC `@K`** (`0x1B 40 4B`); legacy TAPR `kiss on` only as fallback |
| 6 | Keep the **same process** owning the port for KISS DATA |
KISS exit (if needed): `kiss off`, or frame `0xC0 0xFF 0xC0`, or firmware `RESTART`.
### 2.3 Boot-wait (cold start)
After mains loss or cold power without DTR, some boards (notably Landolt TNC2C) need **boot-wait with DTR high during power-on**:
```bash
cd stacks/tncs
./tnc2c-boot-wait.sh /dev/ttyS4 # keep script open; power OFF 10s → ON
# then start the stack immediately so DTR stays high
```
Quiet AF / closed squelch (CD off) during boot improves banner reliability.
---
## 3. Power-cycle history — and the correct path
| Era | Practice | Status |
|-----|----------|--------|
| Former | Power-cycle + boot-wait for almost every KISS/host recovery | Still valid as **rescue** |
| Root cause (hardware) | Landolt: DTR must be high **at** power-on; late open → echo/transparent | Not fully soft-fixable |
| Root cause (host) | Recovery answers lost if an RX thread races the drain; no escalation to boot-wait | Fixed / mitigated in product recovery path |
| Correct default | **Software recovery ladder first**; power-cycle only if echo-only after ladder | Prefer `tnc_serial_recovery` / `tnc2c-host-reset` |
### Software-first ladder (summary)
1. DTR+RTS high, passive listen
2. KISS return `0xC0 0xFF 0xC0`
3. JHOST 0 flush (`^Q^X` + nulls + `JHOST 0`)
4. ESC `V` / ESC `QRES` (software cold boot; DTR stays high)
5. Legacy `kiss off` + `INFO` (last resort)
6. KISS entry ESC `@K`
Success markers: `TheFirmware`, `NORD`, `Version 2.7`, `Checksum`, `cmd:`.
| If… | Then… |
|-----|--------|
| Ladder fails → echo only | `./tnc2c-boot-wait.sh` + power OFF→ON with DTR held |
| Standalone reset OK, stack FAIL | Check dual openers / RX race / start order |
| After boot-wait | Start stack **immediately** (avoid DTR drop gap) |
Detail: `stacks/tncs/docs/TNC-RECOVERY.md`.
---
## 4. Common failure modes
| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| Garbage / no banner | Wrong baud or 7E1 | Match profile (e.g. TNC2C **19200 8N1**) |
| Echo only (`INFO`→`INFO`) | No terminal mode; DTR missed at power-on | Recovery ladder → else boot-wait + power-cycle |
| Binary noise, no commands | Stuck in KISS | KISS return frame or `kiss off` |
| Intermittent recovery | Second process on same tty | One owner; kill minicom/`fuser` |
| Silent TX after good session (modem) | **Stale KISS PTY** (max25-bcprd recycled outside max25d) | Restart **max25d only** (`auto_start` owns max25-bcprd) |
| TX blocked with Soft-DCD busy | `fulldup=no` + Soft-DCD holds channel | Bench: `fulldup=yes`; production CSMA prove-out later |
| Closed squelch ≠ TX fail | No RX noise does not prove PTT failure | Separate MCR/keying from mic wiring |
---
## 5. MAX25-Stack
### 5.1 Paths
| Path | Role |
|------|------|
| **TNC** | `stacks/tncs/` · devices `tnc2c` / `pktnc2` / … · KISS-serial backend |
| **BayCom/based** | `stacks/max25-bcpr/` · `[features] max25_bcpr=yes` · device **`max25e0`** |
| **CRDOP** | `stacks/crdop/` · soft-modem (out of scope here beyond role table) |
### 5.2 Device face `max25e0`
| Surface | Name |
|---------|------|
| Product device id | **`max25e0`** |
| INI mapping | `max25e0 = max25-bcpr:bc0` (right side = **backend:port tag**, not product id) |
| Multi-dev | Always forks **`max25e0:bcN`** |
| Forbidden ids | `bcpr`, `bcpr-bc0`, `bcpr-bc1`, any `bcpr-*` as product face |
| `max25e*` family | **MAX25-Stack only** |
### 5.3 Start
```bash
# Feature (site or ./local — never commit live secrets)
# [features]
# max25_bcpr = yes
#
# [devices]
# max25e0 = max25-bcpr:bc0
./scripts/run-max25d.sh # escalates root only when ttyS/USB/SER12 needs it
./scripts/run-max25-terminal.sh -U /run/max25/modem.sock
```
| Rule | Value |
|------|-------|
| Preferred ownership | **max25d** starts/stops max25-bcprd (`auto_start`) |
| Do not | Recycle `max25-bcprd` under a live max25d (stale PTY) |
| Root | **Only when necessary** (SER12 / ioperm / `/dev/port` / ttyS bind) — TCP/unix-only may stay unprivileged |
| Drop | After privileged init, drop to `[daemon] user=` / `group=` when set |
Site INI examples: `/etc/max25/max25d.ini`, `/etc/max25/max25-bcpr.ini`. Tree secrets: `./local/`.
### 5.4 RX before TX (live RF)
Prove **RX** before live **`--tx`** / on-air SEND:
| Class | RX proof |
|-------|----------|
| BayCom/based | Soft-DCD / noise activity or decoded KISS RX |
| TNC | CONNECT + STATUS (RX ready) before SEND |
| Offline L0 | Encode/decode without PTT — allowed |
`--force-tx` is debug-only. See [TX-RX-TEST.md](docs/TX-RX-TEST.md).
### 5.5 Minimal matrices (no duplication)
| Goal | Doc |
|------|-----|
| Day-to-day ops | [MAX25-OPERATOR-RUNBOOK.md](docs/MAX25-OPERATOR-RUNBOOK.md) |
| BayCom/based / bcpr | [BAYCOM.md](docs/BAYCOM.md) · [BAYCOM-FREEZES.md](docs/BAYCOM-FREEZES.md) |
| Linear operator guide | [MASTER-GUIDE.md](docs/MASTER-GUIDE.md) |
| TNC software recovery | [stacks/tncs/docs/TNC-RECOVERY.md](stacks/tncs/docs/TNC-RECOVERY.md) |
| Device model | [PLUGINS-DEVICE-MODEL.md](docs/PLUGINS-DEVICE-MODEL.md) |
| Doc index | [docs/README.md](docs/README.md) |
---
## 6. Quick decision tree
```text
Need KISS on a UART TNC?
├─ Port free + correct baud?
│ └─ no → fix ownership / baud
├─ Terminal / cmd: present?
│ ├─ no → software recovery ladder
│ │ └─ still echo only → boot-wait + power-cycle (DTR high)
│ └─ yes → ESC @K (or profile kiss entry) → keep one owner
└─ BayCom/based?
└─ [features] max25_bcpr=yes · max25e0 · run-max25d.sh · RX proof → then TX
```
|