tribble-control 0.4.4
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.
- checksums.yaml +7 -0
- data/DESIGN.md +871 -0
- data/LICENSE +21 -0
- data/README.md +541 -0
- data/examples/tribble.conf +208 -0
- data/examples/vbus-check +140 -0
- data/exe/tribble-control +14 -0
- data/lib/tribble-control/cli/connect.rb +282 -0
- data/lib/tribble-control/cli/flash.rb +102 -0
- data/lib/tribble-control/cli/reset.rb +39 -0
- data/lib/tribble-control/cli/serial.rb +60 -0
- data/lib/tribble-control/cli/usb.rb +104 -0
- data/lib/tribble-control/cli.rb +1230 -0
- data/lib/tribble-control/hub/exsys.rb +248 -0
- data/lib/tribble-control/hub/usb.rb +418 -0
- data/lib/tribble-control/hub.rb +115 -0
- data/lib/tribble-control/platform.rb +271 -0
- data/lib/tribble-control/tally.rb +91 -0
- data/lib/tribble-control/version.rb +8 -0
- data/lib/tribble-control.rb +32 -0
- data/man/man1/tribble-control.1 +1483 -0
- data/tribble-control.gemspec +99 -0
- metadata +192 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stephane D'Alu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
# tribble-control
|
|
2
|
+
|
|
3
|
+
Power, flash and monitor the boards plugged into a switchable USB hub,
|
|
4
|
+
from the host that owns the hub.
|
|
5
|
+
|
|
6
|
+
Two kinds of hub. The ExSYS 16-port managed hub, the default, switches
|
|
7
|
+
VBUS on each of its sixteen sockets independently, over an FT232 serial
|
|
8
|
+
line internal to the hub; `tribble-control` drives that line. Any
|
|
9
|
+
standard USB hub with per-port power switching is the other (`hub =
|
|
10
|
+
usb`), switched on the bus itself with hub-class requests through
|
|
11
|
+
`usbconfig`, and so on FreeBSD only for now. For a socket carrying a
|
|
12
|
+
board with a debug probe the tool also speaks SWD through openocd and
|
|
13
|
+
reads the board's console over USB CDC. Everything else on the hub is a
|
|
14
|
+
load it can switch and nothing more.
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
host owning the hub
|
|
18
|
+
│
|
|
19
|
+
┌───────────────┴───────────────┐
|
|
20
|
+
│ │
|
|
21
|
+
the control path: USB data: the SWD probe
|
|
22
|
+
an FT232 line inside and the CDC console
|
|
23
|
+
the hub, or the bus itself
|
|
24
|
+
│ │
|
|
25
|
+
▾ ▾
|
|
26
|
+
┌───────────────────────────────────────────────┐
|
|
27
|
+
│ ExSYS 16-port managed hub, or any hub │
|
|
28
|
+
│ that switches its own ports, 1 to N │
|
|
29
|
+
└───┬───────┬───────┬───────┬─────────┬─────────┘
|
|
30
|
+
│ │ │ │ │
|
|
31
|
+
▾ ▾ ▾ ▾ ▾
|
|
32
|
+
board board board ... load the hub
|
|
33
|
+
only powers
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
A file you write — the **configuration** — says which board sits on which
|
|
37
|
+
port, how to reach it, and which ports must never lose power. The tool
|
|
38
|
+
switches the bench it is told about and nothing else.
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
tribble-control -C tribble.conf usb status
|
|
42
|
+
tribble-control -C tribble.conf flash zephyr.hex alpha beta
|
|
43
|
+
tribble-control -C tribble.conf connect --off gamma | tee run.log
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The full manual — every option, every configuration key, the hub protocol,
|
|
47
|
+
recipes and traps — is the man page. This file is the short way in.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Requirements
|
|
51
|
+
|
|
52
|
+
`tribble-control` runs on the machine owning the hub, not on a
|
|
53
|
+
workstation: it reaches the hub over something plugged into that
|
|
54
|
+
machine — an FT232 line, or the USB bus itself — and needs a local
|
|
55
|
+
openocd to reach a board.
|
|
56
|
+
|
|
57
|
+
* **Ruby 3.1** or later.
|
|
58
|
+
* **openocd**, for every command that reaches a board over SWD:
|
|
59
|
+
`flash`, `reset` and `connect --reset`. It is not a gem and
|
|
60
|
+
installing the gem will not bring it — `pkg install openocd`,
|
|
61
|
+
`apt install openocd`. It is expected at `/usr/bin/openocd`;
|
|
62
|
+
elsewhere pass `--openocd=/usr/local/bin/openocd`, or
|
|
63
|
+
`--openocd=openocd` to have `PATH` answer. `flash` and `reset`
|
|
64
|
+
check for it before they switch a single port.
|
|
65
|
+
* **Linux or FreeBSD.** Every command works on both, by every
|
|
66
|
+
selection method. The two hosts answer "where is this board
|
|
67
|
+
plugged in" differently — Linux states it in `/sys/bus/usb`, and
|
|
68
|
+
FreeBSD is asked to walk its sysctl tree instead — and the one
|
|
69
|
+
thing FreeBSD cannot do is see a device that no driver claimed,
|
|
70
|
+
there being no node for it at all. A probe that enumerates and
|
|
71
|
+
attaches nothing is invisible there rather than serial-less.
|
|
72
|
+
|
|
73
|
+
One step has never run on real hardware: on FreeBSD, placing an
|
|
74
|
+
ExSYS hub's sockets in the USB tree, which `--method usb` (`connect`,
|
|
75
|
+
`serial`) builds a board's path from. It is derived from where the
|
|
76
|
+
hub's FT232 control line sits, and has only been tested against a
|
|
77
|
+
recorded sysctl tree; on Linux the same geometry was checked against a
|
|
78
|
+
live hub. Its failure would be a console opened on the wrong board,
|
|
79
|
+
so the first time on a FreeBSD host with an ExSYS hub, check that
|
|
80
|
+
`connect` names the board you expect, or use `--method serial`.
|
|
81
|
+
* **For `hub = usb`: FreeBSD, and membership of group `operator`.**
|
|
82
|
+
That hub is switched with `usbconfig` hub-class requests, which is
|
|
83
|
+
FreeBSD's command — Linux has none that issues an arbitrary control
|
|
84
|
+
request to a hub — so the backend refuses to open on any other host.
|
|
85
|
+
Root is not required: the ugen nodes are `root:operator` 0660, and
|
|
86
|
+
the kernel demands the driver privilege only for SET_ADDRESS,
|
|
87
|
+
SET_CONFIG and SET_INTERFACE, so a hub-class port feature request
|
|
88
|
+
passes. `pw groupmod operator -m <user>`, then log in again.
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
## Installing it
|
|
92
|
+
|
|
93
|
+
Install it on the hub host as a gem, which brings the Ruby
|
|
94
|
+
dependencies with it and needs nothing else:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
gem install tribble-control # a release, from rubygems.org
|
|
98
|
+
rake install # from a checkout
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A release is what to install anywhere results are recorded: its version
|
|
102
|
+
names exactly one tree. A checkout built with `rake install` carries
|
|
103
|
+
the number of the last release whatever has been committed since, so
|
|
104
|
+
`tribble-control --version` cannot tell the two apart.
|
|
105
|
+
|
|
106
|
+
Or, on a host that should not gain gems system-wide, deploy the
|
|
107
|
+
checkout and vendor the bundle beside it:
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
bundle install --path vendor
|
|
111
|
+
bundle binstubs tribble-control
|
|
112
|
+
./bin/tribble-control --man
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The binstub sets that bundle up before loading anything, so it is what
|
|
116
|
+
such a host should call.
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
## The configuration
|
|
120
|
+
|
|
121
|
+
Nearly every command needs one, given with `-C`/`--config`. Without
|
|
122
|
+
it, a file named `tribble-control.conf` in the current directory is
|
|
123
|
+
used if there is one; neither the home directory nor `/etc` is ever
|
|
124
|
+
consulted, and giving `-C` turns that lookup off. It describes a setup
|
|
125
|
+
rather than the tool, so it lives with whatever owns the setup;
|
|
126
|
+
`examples/tribble.conf` is a commented file to start from. Deploy the
|
|
127
|
+
two together — they are read together, and a hub with one of them
|
|
128
|
+
fresh and the other stale switches the wrong ports.
|
|
129
|
+
|
|
130
|
+
If the two must move separately, move the **tool first**. Every
|
|
131
|
+
top-level key this tool does not know is taken for a board, and a board
|
|
132
|
+
must declare a port, so a configuration carrying a newer key meets an
|
|
133
|
+
older tool as `devlist entry 'device' has no port`. That is a refusal
|
|
134
|
+
before anything is switched rather than a bench in the wrong state —
|
|
135
|
+
but the command does not run, so upgrade in that order.
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
# Which kind of hub, and which one. 'exsys' is the default: an ExSYS
|
|
139
|
+
# hub, named by its FT232's serial number. 'usb' is any hub with
|
|
140
|
+
# per-port power switching, named by its own serial, and only that kind
|
|
141
|
+
# takes a 'switch' line.
|
|
142
|
+
hub = exsys
|
|
143
|
+
device = AL03GD7X
|
|
144
|
+
|
|
145
|
+
# What must never be powered down: ports by number, entries by name,
|
|
146
|
+
# and every port the file does not mention unless 'undeclared = no'.
|
|
147
|
+
protect {
|
|
148
|
+
ports = [ 13, 14, 15, 16 ]
|
|
149
|
+
nodes = [ rpi ]
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# What a KIND of board is, said once instead of on every board.
|
|
153
|
+
types {
|
|
154
|
+
nrf52840-mdk {
|
|
155
|
+
interface = cmsis-dap # openocd interface script: which probe
|
|
156
|
+
target = nrf52 # openocd target script: which chip
|
|
157
|
+
baud = 230400 # console speed
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# Every other key is a device. `port` is required.
|
|
162
|
+
alpha {
|
|
163
|
+
type = nrf52840-mdk
|
|
164
|
+
serial = '102636...97969902' # the PROBE's serial, pasted whole
|
|
165
|
+
port = 1
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# No serial: still switchable, still has a console, but it cannot be
|
|
169
|
+
# reset or flashed in parallel.
|
|
170
|
+
gamma {
|
|
171
|
+
port = 2
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# Something the bench only feeds. Declaring it is what lets 'protect'
|
|
175
|
+
# name it, and what puts 'rpi' in the 'usb status' row for port 12.
|
|
176
|
+
rpi {
|
|
177
|
+
port = 12
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# `port = none` keeps the record of a board that has left the bench.
|
|
181
|
+
# It is never selected, switched or flashed, and its serial survives.
|
|
182
|
+
retired {
|
|
183
|
+
serial = '102636...97969902'
|
|
184
|
+
port = none
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
A device's own keys win over the type's. Read a probe serial off a
|
|
189
|
+
powered board with `tribble-control serial <name>` and paste it whole:
|
|
190
|
+
48 hex characters for CMSIS-DAP, 12 for J-Link.
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
## Which hub
|
|
194
|
+
|
|
195
|
+
Two things to settle: which *kind* of hub, and which hub of that kind.
|
|
196
|
+
|
|
197
|
+
The kind is `hub =` at the top of the configuration, or `--hub` on the
|
|
198
|
+
command line: `exsys` (the default) or `usb`. It names what the hub
|
|
199
|
+
**is**, not which tool drives it on this host, so the same
|
|
200
|
+
configuration line keeps working the day a Linux implementation lands.
|
|
201
|
+
It cannot be read off the
|
|
202
|
+
`device =` value, either — a USB path such as `1-1.1` names an FT232's
|
|
203
|
+
socket for an ExSYS hub and the hub itself for a usb hub — so it has to
|
|
204
|
+
be said.
|
|
205
|
+
|
|
206
|
+
An ExSYS hub on a host with one hub needs to be told nothing: the tool
|
|
207
|
+
looks for the FTDI 0403:6001 that is a hub's control adapter and drives
|
|
208
|
+
the one it finds.
|
|
209
|
+
|
|
210
|
+
A host with two is two benches, and it refuses to guess — that FTDI id
|
|
211
|
+
is every FT232 on the machine, so the first one enumerated is a coin
|
|
212
|
+
toss, and a command that reaches the wrong hub is not an error
|
|
213
|
+
anywhere: the ports exist, the frames are accepted, and the boards that
|
|
214
|
+
go dark are on the other bench. It lists what it found instead, with
|
|
215
|
+
serials:
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
tribble-control: unable to auto-detect the hub control line: 2 FTDI
|
|
219
|
+
0403:6001 adapters on this host (found: A50285BI on /dev/ttyUSB0,
|
|
220
|
+
AL03GD7X on /dev/ttyUSB1). Name the one to drive with -d, or with a
|
|
221
|
+
'device =' line in the configuration.
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`exsys-usb discover`, from the exsys gem, lists them the same way
|
|
225
|
+
without switching anything:
|
|
226
|
+
|
|
227
|
+
```text
|
|
228
|
+
/dev/ttyUSB0 A50285BI 1-1.2.4.4
|
|
229
|
+
/dev/ttyUSB1 AL03GD7X 1-1.3.4.4
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Put that serial in the configuration, as `device =` at the top, and
|
|
233
|
+
`-C` alone selects a bench — which is already the thing every command
|
|
234
|
+
has to say. `-d` overrides it for the one-off.
|
|
235
|
+
|
|
236
|
+
Either takes three shapes, told apart by what they look like:
|
|
237
|
+
|
|
238
|
+
| Written | Means | Stays with |
|
|
239
|
+
| :------------- | :--------------------------- | :----------- |
|
|
240
|
+
| `AL03GD7X` | the FT232's serial number | the adapter |
|
|
241
|
+
| `1-1.2.4.4` | a USB path on this host | the socket |
|
|
242
|
+
| `/dev/ttyUSB1` | the serial line itself | nothing |
|
|
243
|
+
|
|
244
|
+
The last is the one not to write down. The `1` in `/dev/ttyUSB1` is
|
|
245
|
+
not the hub's number, and not the USB device number either — it is the
|
|
246
|
+
usbserial layer's index, and it is the lowest one free when that
|
|
247
|
+
adapter is probed. So it depends on what else attached first, and it
|
|
248
|
+
is reused: unplug whatever holds `ttyUSB0` and the next thing to attach
|
|
249
|
+
takes `ttyUSB0`. Two hubs can swap names across a reboot, or while the
|
|
250
|
+
machine is up.
|
|
251
|
+
|
|
252
|
+
Between the other two: a serial names *this particular hub* and follows
|
|
253
|
+
it to another socket or another machine, which is usually what a bench
|
|
254
|
+
wants. A USB path names *whatever is plugged into that socket*, a
|
|
255
|
+
replacement hub included. Reach for the path when the hub's EEPROM
|
|
256
|
+
carries no serial — then it is the only stable name it has — or when
|
|
257
|
+
the socket is the fixed thing. Both platforms report one, by different
|
|
258
|
+
means: Linux states it in `/sys`, and on FreeBSD it is walked out of
|
|
259
|
+
the sysctl tree. The numbering is each host's own, so a path names a
|
|
260
|
+
socket on the machine that reported it and does not travel.
|
|
261
|
+
|
|
262
|
+
A `hub = usb` is named the same three ways, in its own shapes:
|
|
263
|
+
|
|
264
|
+
| Written | Means | Stays with |
|
|
265
|
+
| :------------- | :-------------------------- | :---------- |
|
|
266
|
+
| `AC0528515619` | the hub's own serial number | the hub |
|
|
267
|
+
| `1-1.1` | a USB path on this host | the socket |
|
|
268
|
+
| ugen1.4 | that device, used as given | nothing |
|
|
269
|
+
|
|
270
|
+
A ugen name is this kind's `/dev/ttyUSB1` and carries the same warning:
|
|
271
|
+
the number is enumeration order — ugen1.4 is the fourth device the
|
|
272
|
+
second controller attached — so a replug renumbers it, and a
|
|
273
|
+
configuration naming a hub that way points at whatever attached in its
|
|
274
|
+
place. Keep it
|
|
275
|
+
for the one-off; write the serial, or the path when the socket is the
|
|
276
|
+
fixed thing.
|
|
277
|
+
|
|
278
|
+
Auto-detection refuses the same way: one candidate is taken, two or more
|
|
279
|
+
are listed and the command stops, each candidate with its serial, its
|
|
280
|
+
ugen name, its USB path and its port count — the count being the only
|
|
281
|
+
thing that tells two of the same part apart when neither has a serial.
|
|
282
|
+
Root hubs are never candidates. They are the controller a host's own
|
|
283
|
+
sockets hang off, and a port of one has no `PORT_POWER` to clear, so
|
|
284
|
+
offering one would offer a hub every command against it then failed on.
|
|
285
|
+
|
|
286
|
+
The ports are the hub's own: 1 to the `bNbrPorts` its hub descriptor
|
|
287
|
+
reports, read once when the hub is opened. The fixed sixteen is the
|
|
288
|
+
ExSYS hub's alone, and `protect { undeclared = yes }` earns its keep
|
|
289
|
+
here — on a dock, one hub port feeds the next hub in the chain and
|
|
290
|
+
another feeds the Ethernet adapter, and neither is a port to sweep off.
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
## What "off" does
|
|
294
|
+
|
|
295
|
+
The ExSYS hub cuts VBUS: `usb off` takes the power away from the socket
|
|
296
|
+
and the board on it stops. A standard hub may do that, or may only take
|
|
297
|
+
the port off the bus, depending on whether a power switch is wired to
|
|
298
|
+
the socket at all — and no software can tell the two apart, because a
|
|
299
|
+
hub with none still reports the port unpowered and drops the link, so
|
|
300
|
+
the device vanishes from the host and comes back either way. The
|
|
301
|
+
operator says which, with `switch =` at the top of a `hub = usb`
|
|
302
|
+
configuration:
|
|
303
|
+
|
|
304
|
+
| Written | Means |
|
|
305
|
+
| :-------------- | :----------------------------------------------- |
|
|
306
|
+
| `switch = link` | the default: `off` takes the port off the bus |
|
|
307
|
+
| `switch = vbus` | `off` cuts the socket's power |
|
|
308
|
+
|
|
309
|
+
On an ExSYS hub the key is refused rather than ignored: that hub always
|
|
310
|
+
cuts power, and a line that changes nothing is a line somebody will
|
|
311
|
+
trust.
|
|
312
|
+
|
|
313
|
+
Find out which yours is by watching a board's LED. Put a board that
|
|
314
|
+
lights up on a port, run `usb off` for that port, and look: an LED that
|
|
315
|
+
goes out means `vbus`, an LED that stays lit while the board disappears
|
|
316
|
+
from the host means `link`. Test the socket you will actually use — the
|
|
317
|
+
USB 2 and USB 3 sides of one socket are different ports on different
|
|
318
|
+
hubs, and a hub may switch neither.
|
|
319
|
+
`examples/vbus-check` runs the recipe: give it the options you would
|
|
320
|
+
give `tribble-control` and the port, it cuts the port for five seconds,
|
|
321
|
+
restores it even on Ctrl-C, and prints the `switch =` line your answer
|
|
322
|
+
implies.
|
|
323
|
+
|
|
324
|
+
Under `switch = link` everything that powers down still works. `usb
|
|
325
|
+
off`, `toggle` and `set` behave, the board vanishes from the host
|
|
326
|
+
exactly as a power cut would, and `--method power` still identifies a
|
|
327
|
+
board by being the only one openocd can see. What does not happen is
|
|
328
|
+
the board restarting. So every power-down warns once, naming the ports
|
|
329
|
+
that stay powered:
|
|
330
|
+
|
|
331
|
+
```text
|
|
332
|
+
ugen1.4 cuts the link, not the power: the board(s) on port(s) 1 2 stay
|
|
333
|
+
powered (switch = link)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
and the after-flash power cycle (`power_cycle = after-flash`, the
|
|
337
|
+
DWM1001's trap) is skipped with a warning rather than pretended — a
|
|
338
|
+
cycle that only re-enumerates the probe leaves the board in the very
|
|
339
|
+
state the cycle exists to clear.
|
|
340
|
+
|
|
341
|
+
After every switch the port's status is read back, and the command fails
|
|
342
|
+
if the power bit did not follow. That read-back is the tool's only
|
|
343
|
+
measurement of whether a hub switches at all: a hub that accepts
|
|
344
|
+
`CLEAR_FEATURE(PORT_POWER)`, answers OK and leaves the port up would
|
|
345
|
+
otherwise have `usb off` report success on a bench it never touched.
|
|
346
|
+
Nothing is refused on what a hub *declares* about its switching, for the
|
|
347
|
+
same reason — the dock's Genesys hub declares ganged and switches per
|
|
348
|
+
port anyway.
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
## Protected ports
|
|
352
|
+
|
|
353
|
+
A hub port carries whatever is plugged into it, and cutting VBUS on a
|
|
354
|
+
single-board computer reboots it mid-write. So powering a port *down*
|
|
355
|
+
is guarded, and powering one *up* is not:
|
|
356
|
+
|
|
357
|
+
* Ports the configuration does not mention are protected. `protect {
|
|
358
|
+
undeclared = no }` lifts that for the unmentioned ones.
|
|
359
|
+
* Ports `protect` names are protected whichever mode is in force —
|
|
360
|
+
`ports` for a number, `nodes` for the name of a configuration entry,
|
|
361
|
+
which protects whichever port that entry says the board is on.
|
|
362
|
+
* `-F`/`--force` lifts both. Without a configuration at all,
|
|
363
|
+
`tribble-control` refuses to power anything down.
|
|
364
|
+
|
|
365
|
+
The two keys this replaced — `reserved`, and `undeclared` at the top of
|
|
366
|
+
the file — are refused by name, each with the line to write instead. A
|
|
367
|
+
configuration written for 0.2.0 or earlier does not load and does not
|
|
368
|
+
switch anything until its protection is rewritten as the block above.
|
|
369
|
+
|
|
370
|
+
`usb status` prints the hub's own view next to the configuration's, so
|
|
371
|
+
you can see what is on and what may be switched before switching it.
|
|
372
|
+
The protection is only ever as current as the file: a stale
|
|
373
|
+
configuration guards the ports it used to know about.
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
## Commands
|
|
377
|
+
|
|
378
|
+
| Command | What it does |
|
|
379
|
+
| :-------- | :-------------------------------------------------- |
|
|
380
|
+
| `usb` | Port power: `status`, `on`, `off`, `toggle`, `set`. |
|
|
381
|
+
| `serial` | Print a board's debug-probe serial number. |
|
|
382
|
+
| `flash` | Write a firmware image to one or more boards. |
|
|
383
|
+
| `reset` | Reboot boards over SWD. |
|
|
384
|
+
| `connect` | Open board consoles, print their lines, summarise. |
|
|
385
|
+
|
|
386
|
+
Every command takes device names or port numbers interchangeably, and
|
|
387
|
+
acts on every board on the bench when given neither (each declared
|
|
388
|
+
device but those with `port = none`). Note that `serial`
|
|
389
|
+
prints a probe's identifying number, not console text — reading a
|
|
390
|
+
board's console is `connect`.
|
|
391
|
+
|
|
392
|
+
Naming a board is not the same as telling openocd which one to talk
|
|
393
|
+
to, and `-m`/`--method` is how that is decided. Each command accepts
|
|
394
|
+
only the methods that make sense for it and uses the first as its
|
|
395
|
+
default:
|
|
396
|
+
|
|
397
|
+
| Method | Addresses a board by | Needs |
|
|
398
|
+
| :------- | :------------------------- | :----------------- |
|
|
399
|
+
| `serial` | its debug probe's serial | `serial =` on each |
|
|
400
|
+
| `usb` | its USB path under the hub | a visible topology |
|
|
401
|
+
| `power` | being the only one powered | nothing |
|
|
402
|
+
|
|
403
|
+
| Command | Methods accepted | Default |
|
|
404
|
+
| :-------- | :---------------------- | :------- |
|
|
405
|
+
| `usb` | none — it acts on ports | — |
|
|
406
|
+
| `serial` | `usb`, `power` | `usb` |
|
|
407
|
+
| `flash` | `serial`, `power` | `serial` |
|
|
408
|
+
| `reset` | `serial` | `serial` |
|
|
409
|
+
| `connect` | `usb`, `serial` | `usb` |
|
|
410
|
+
|
|
411
|
+
`serial` is the default for `flash` and `reset` because it is the fast
|
|
412
|
+
one: every board stays powered and they are done at the same time, one
|
|
413
|
+
openocd apiece. It needs a `serial =` on each selected board, and
|
|
414
|
+
aborts if one is missing.
|
|
415
|
+
|
|
416
|
+
`power` is the fallback that needs no configuration: it identifies a
|
|
417
|
+
board by being the only one powered. It costs a power cycle per board
|
|
418
|
+
and **leaves the bench powered off** when it finishes — every declared
|
|
419
|
+
board whose port may be switched, that is; one the configuration
|
|
420
|
+
protects stays powered and says so. Run `usb on` afterwards to bring
|
|
421
|
+
the bench back up. A probe left powered on such a port would be a
|
|
422
|
+
second adapter in front of openocd, so a run that finds more than one
|
|
423
|
+
probe console once a board is up is refused.
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
## Reading a console
|
|
427
|
+
|
|
428
|
+
`connect` opens the console of each selected board, prefixes every line
|
|
429
|
+
with the board's name, and prints it. What those lines *mean* is not
|
|
430
|
+
this tool's business — the strings worth counting belong to whatever
|
|
431
|
+
firmware is on the bench this month. So `connect` hands each line to a
|
|
432
|
+
**tally**, which counts whatever it likes and writes the `SUMMARY`
|
|
433
|
+
line.
|
|
434
|
+
|
|
435
|
+
Two tallies ship: `lines` counts lines, and `none` writes no summary at
|
|
436
|
+
all. Anything that knows a firmware's strings is a block registered by
|
|
437
|
+
a Ruby file named with `-r`/`--require` and chosen with `tally =` in
|
|
438
|
+
the configuration, for the whole bench or for one board. A run can
|
|
439
|
+
override the configuration with `connect --tally NAME` for every board it
|
|
440
|
+
captures, or `--tally DEV=NAME` for one, which is how a board reflashed
|
|
441
|
+
with other firmware is read without editing the file:
|
|
442
|
+
|
|
443
|
+
```sh
|
|
444
|
+
tribble-control -C tribble.conf -r twr-tally.rb connect --tally twr --tally D4=none
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
See DESIGN.md and the manual's TALLIES section.
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
## The manual
|
|
451
|
+
|
|
452
|
+
The full manual is a man page, `man/man1/tribble-control.1`, and ships
|
|
453
|
+
inside the gem.
|
|
454
|
+
|
|
455
|
+
```sh
|
|
456
|
+
tribble-control --man # renders the shipped page, wherever it is
|
|
457
|
+
rake man:install # PREFIX=/usr/local, for real `man` access
|
|
458
|
+
mandoc man/man1/tribble-control.1
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
`--man` renders the page with the first of `mandoc`, `groff` or `nroff`
|
|
462
|
+
it finds, pages it when the output is a terminal, and hands plain text
|
|
463
|
+
to a pipe. RubyGems installs nothing outside the gem directory, so
|
|
464
|
+
`gem install` alone will not make `man tribble-control` work; the page
|
|
465
|
+
ships at `man/man1/` inside the gem precisely so the gem's `man`
|
|
466
|
+
directory can serve as a `MANPATH` entry if you would rather not copy
|
|
467
|
+
it anywhere.
|
|
468
|
+
|
|
469
|
+
`--help` lists the commands, and `CMD --help` the options of one.
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
## Tests
|
|
473
|
+
|
|
474
|
+
```sh
|
|
475
|
+
rake # everything that needs no hub, and the linter
|
|
476
|
+
rake test # the minitest suite: configuration, types, tallies, the
|
|
477
|
+
# openocd command line, the ExSYS hub exchange against
|
|
478
|
+
# a pty emulator and the usb one against a fake
|
|
479
|
+
# usbconfig
|
|
480
|
+
rake lint # rubocop, gating at zero offences
|
|
481
|
+
rake test:bench # the regression suite, which needs the bench
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
The bench suite drives a **deployed** copy over ssh, because that is
|
|
485
|
+
the machine with the hub on the end of a serial line. There is no
|
|
486
|
+
default host — one lab's address does not belong in a tool meant to
|
|
487
|
+
drive any ExSYS hub — so it refuses to start without one:
|
|
488
|
+
|
|
489
|
+
```sh
|
|
490
|
+
sh test/test-tribble-control HOST [tribble-control-path] [tally-path]
|
|
491
|
+
TRIBBLE_HOST=<host> rake test:bench # also TRIBBLE_PATH, TRIBBLE_TALLY
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
It is written to be safe to run on a live bench: nothing writes flash
|
|
495
|
+
or switches a port off unless `TRIBBLE_TEST_FLASH=1` asks for the
|
|
496
|
+
power-cycle gate. One test resets a board deliberately, as a positive
|
|
497
|
+
control, and the `connect` and `flash` tests power the ports of the
|
|
498
|
+
boards they name up, which is the benign direction.
|
|
499
|
+
|
|
500
|
+
The second argument points the suite at a different copy of the tool —
|
|
501
|
+
a deliberately broken one, say, since a test that has never been seen
|
|
502
|
+
to fail is not evidence. The third names the tally, which any command
|
|
503
|
+
that opens a console needs when the configuration asks for one by name.
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
## Hacking on it
|
|
507
|
+
|
|
508
|
+
DESIGN.md is the companion to this file: how the pieces fit, and where
|
|
509
|
+
the seams are for a new board family, a new probe, a new host platform,
|
|
510
|
+
a new command or a new tally.
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
## The name
|
|
514
|
+
|
|
515
|
+
Tribbles are the small, furry, gentle and extremely numerous creatures
|
|
516
|
+
of ["The Trouble with Tribbles"][episode] (*Star Trek*, 1967, written by
|
|
517
|
+
David Gerrold). McCoy works out why there are so many: they are born
|
|
518
|
+
pregnant, and spend over half their metabolism reproducing. By the end
|
|
519
|
+
of the episode they have filled the ship, and Kirk is [shoulder-deep in
|
|
520
|
+
them][kirk].
|
|
521
|
+
|
|
522
|
+
A bench fills up the same way. One board becomes three, three become
|
|
523
|
+
eleven, they are identical, every one of them wants power, and not one
|
|
524
|
+
of them will tell you which socket it is sitting in. That last part is
|
|
525
|
+
what the configuration is for.
|
|
526
|
+
|
|
527
|
+
![Tribble props from the Star Trek exhibit at the Henry Ford Museum][photo]
|
|
528
|
+
|
|
529
|
+
Photo by Joe Ross, [CC BY-SA 2.0][licence], via [Wikimedia Commons][page].
|
|
530
|
+
|
|
531
|
+
[episode]: https://en.wikipedia.org/wiki/The_Trouble_with_Tribbles
|
|
532
|
+
[kirk]: https://en.wikipedia.org/wiki/Tribble#/media/File:ST_TroubleWithTribbles.jpg
|
|
533
|
+
[photo]: https://commons.wikimedia.org/wiki/Special:FilePath/Tribbles!_-_Star_Trek_-_Exploring_New_Worlds_Exhibit_at_the_Henry_Ford_Museum.jpg?width=480
|
|
534
|
+
[page]: https://commons.wikimedia.org/wiki/File:Tribbles!_-_Star_Trek_-_Exploring_New_Worlds_Exhibit_at_the_Henry_Ford_Museum.jpg
|
|
535
|
+
[licence]: https://creativecommons.org/licenses/by-sa/2.0/
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
## License
|
|
539
|
+
|
|
540
|
+
MIT. See LICENSE. The photograph above is not mine and is not MIT; it
|
|
541
|
+
carries the CC BY-SA 2.0 licence credited with it.
|