oep-client-python 0.0.5__tar.gz → 0.0.6__tar.gz
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.
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/CHANGELOG.md +8 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/PKG-INFO +1 -1
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/pyproject.toml +1 -1
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/__init__.py +1 -1
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/__main__.py +6 -1
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/config.py +12 -7
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/endpoint.py +28 -10
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/fake.py +2 -2
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/fake_serve.py +2 -2
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/registry.py +4 -4
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/riscv.py +31 -12
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_config.py +44 -1
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_fake_spec.py +39 -4
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/.gitignore +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/LICENSE +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/README.ja.md +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/README.md +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/arm.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/capture.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/catalog.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/ch32_flash.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/cobs.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/console.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/core.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/decode.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/dump.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/esp32_targets.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/fake_serial.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/fixture.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/frames.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/hid_stream.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/host.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/interfaces.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/link.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/message.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/names.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/rp2350.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/target.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/uiapduino.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/src/oep_client/usb_stream.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_capabilities.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_capture.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_cobs.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_esp32_targets.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_flash_console.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_hid_stream.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_interfaces.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_link_host.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_link_serial.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_session.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_target_parts.py +0 -0
- {oep_client_python-0.0.5 → oep_client_python-0.0.6}/tests/test_usb_stream.py +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.0.6
|
|
6
|
+
- (EN) No default reset line (oep-if-debug §3): `Wire.attach_under_reset(channel, ...)` needs its channel, and `Wire.reset_channels()` reads the ones the probe allows (describe role_channels, role reset). The fake takes only those, and not one a plan holds.
|
|
7
|
+
- (JA) 既定の reset 線は無くなった(oep-if-debug §3): `Wire.attach_under_reset(channel, ...)` は channel が必須。probe が許す channel は `Wire.reset_channels()` で読む(describe の role_channels の role reset)。偽の probe はそれだけを受け、plan が持つ channel は断る。
|
|
8
|
+
- (EN) The line's settings are the target's, passed by the host: `attach(..., idle_clock="low")` (rvswd, critical) and the probe.config slot's new max_speed / idle_clock fields (`oep config slot --max-speed --idle-clock`) for the probe's own attach. The slot item's layout changed.
|
|
9
|
+
- (JA) 線の設定は target のもので、host が渡す: `attach(..., idle_clock="low")`(rvswd、critical)と、probe が自分で attach するための probe.config のスロットの max_speed / idle_clock(`oep config slot --max-speed --idle-clock`)。スロットの項目の形が変わった。
|
|
10
|
+
- (EN) The fake probe keeps a plan the settings put in (oep-core §8): plan_release leaves it (n = 0 included) and plan_apply naming its fn is rejected unavailable.
|
|
11
|
+
- (JA) 偽の probe は、設定が入れた plan を設定のものとして扱う(oep-core §8): plan_release はそれを解かず(n = 0 でも)、その fn を挙げた plan_apply は rejected unavailable。
|
|
12
|
+
|
|
5
13
|
## 0.0.5
|
|
6
14
|
- (EN) USB (python-libusb1) streams close cleanly and at exit: the IN transfers are cancelled and taken back by the event thread before the handle and the context go. A program that ended without closing the link hung at exit, or libusb aborted.
|
|
7
15
|
- (JA) USB(python-libusb1)の stream は、終了時にも片付いて閉じる: IN の転送を取り消し、event thread が取り戻してから handle と context を閉じる。link を閉じずに終わったプログラムが終了で固まったり、libusb が abort したりしていた。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: oep-client-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: Open Embedded Probe (OEP) v1 host: serial / USB / TCP transports, the session rules, the standard interfaces, and a fake probe
|
|
5
5
|
Project-URL: Homepage, https://github.com/Open-Embedded-Probe/oep-client-python
|
|
6
6
|
Project-URL: Repository, https://github.com/Open-Embedded-Probe/oep-client-python
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "oep-client-python"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.6"
|
|
8
8
|
description = "Open Embedded Probe (OEP) v1 host: serial / USB / TCP transports, the session rules, the standard interfaces, and a fake probe"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -64,6 +64,10 @@ def _config_parser(sub) -> None:
|
|
|
64
64
|
slot.add_argument("--attach", choices=sorted(config.ATTACH), default="host")
|
|
65
65
|
slot.add_argument("--retry", type=int, default=0, help="at-boot: try again every N s while absent (0: never)")
|
|
66
66
|
slot.add_argument("--mechanism", choices=sorted(config.MECHANISM), default="dmseq")
|
|
67
|
+
slot.add_argument("--max-speed", type=int, default=0,
|
|
68
|
+
help="the line's ceiling in Hz for the probe's own attach (0: none); the target's, e.g. 1000000")
|
|
69
|
+
slot.add_argument("--idle-clock", choices=sorted(config.IDLE_CLOCK), default="high",
|
|
70
|
+
help="rvswd: SWCLK while the line rests (the target's: low on CH32L103 / V203)")
|
|
67
71
|
slot.add_argument("--lock", help="MASK:VALUE (hex u32) the target_id (WCH DMI 0x7F) must match, e.g. ffffff0f:035e0600")
|
|
68
72
|
slot.add_argument("--save", action="store_true", help="save after the change")
|
|
69
73
|
bind = cs.add_parser("bind", help="what a serial port carries")
|
|
@@ -183,7 +187,7 @@ def _config(args) -> int:
|
|
|
183
187
|
mask, _, value = args.lock.partition(":")
|
|
184
188
|
lock = (1, struct.pack("<I", int(mask, 16)), struct.pack("<I", int(value, 16)))
|
|
185
189
|
it = config.Slot(args.slot, fn, _pins(hst, fn, args.pins, args.wire), args.name, args.attach, args.retry,
|
|
186
|
-
args.mechanism, lock)
|
|
190
|
+
args.mechanism, lock, args.max_speed, args.idle_clock)
|
|
187
191
|
_change(hst, cfg, [it], args.save)
|
|
188
192
|
elif args.action == "bind":
|
|
189
193
|
slots = {it.name: it.slot for it in cfg.items() if isinstance(it, config.Slot)}
|
|
@@ -250,6 +254,7 @@ def _show(hst, cfg, as_json: bool) -> int:
|
|
|
250
254
|
s = by_slot.get(it.slot)
|
|
251
255
|
pins = f"{it.pins[0]}" if it.pins[1] == 0xFFFF else f"{it.pins[0]},{it.pins[1]}"
|
|
252
256
|
retry = f" retry {it.retry_s} s" if it.attach == "at-boot" else ""
|
|
257
|
+
retry += (f" max {it.max_speed} Hz" if it.max_speed else "") + (" idle-low" if it.idle_clock == "low" else "")
|
|
253
258
|
lock = (f" lock {int.from_bytes(it.lock[1], 'little'):08x}:{int.from_bytes(it.lock[2], 'little'):08x}"
|
|
254
259
|
if it.lock else "")
|
|
255
260
|
live = ""
|
|
@@ -29,6 +29,7 @@ MECHANISM = {k: v for k, v in reg.TARGET_CONSOLE.enum["mechanism"].items()}
|
|
|
29
29
|
SLOT_STATE = {v: k.replace("_", "-") for k, v in _CFG.enum["slot_state"].items()}
|
|
30
30
|
BIND_FLOW = {v: k for k, v in _CFG.enum["bind_flow"].items()}
|
|
31
31
|
IDLE = {k.replace("_", "-"): v for k, v in _CFG.enum["idle_mode"].items()}
|
|
32
|
+
IDLE_CLOCK = dict(reg.WIRE_RVSWD.enum["idle_clock"]) # a slot's idle_clock (oep-if-debug §3)
|
|
32
33
|
STORAGE_STATE = {v: k for k, v in _CFG.enum["storage_state"].items()}
|
|
33
34
|
|
|
34
35
|
|
|
@@ -79,12 +80,15 @@ class Slot:
|
|
|
79
80
|
retry_s: int = 0 # at-boot: try again every retry_s while the target is not there (0: never)
|
|
80
81
|
mechanism: str = "dmseq" # sdi, dmdata, dmseq
|
|
81
82
|
lock: tuple[int, bytes, bytes] | None = None
|
|
83
|
+
max_speed: int = 0 # the line's ceiling in Hz for the probe's own attach (0: none) - the target's
|
|
84
|
+
idle_clock: str = "high" # rvswd: SWCLK while the line rests, high / low - the target's (oep-if-debug §3)
|
|
82
85
|
TAG = ITEM["slot"]
|
|
83
86
|
|
|
84
87
|
def value(self) -> bytes:
|
|
85
88
|
name = self.name.encode()
|
|
86
|
-
v = struct.pack("<
|
|
87
|
-
self.retry_s if self.attach == "at-boot" else 0,
|
|
89
|
+
v = struct.pack("<BHHHBHIBBB", self.slot, self.wire_fn, *self.pins, ATTACH[self.attach],
|
|
90
|
+
self.retry_s if self.attach == "at-boot" else 0, self.max_speed, IDLE_CLOCK[self.idle_clock],
|
|
91
|
+
MECHANISM[self.mechanism], len(name)) + name
|
|
88
92
|
if self.lock is None:
|
|
89
93
|
return v + b"\x00"
|
|
90
94
|
scheme, mask, value = self.lock
|
|
@@ -127,15 +131,16 @@ def decode(tag: int, v: bytes):
|
|
|
127
131
|
return Label(struct.unpack_from("<H", v)[0], v[2:].decode("utf-8", "replace"))
|
|
128
132
|
if tag == ITEM["idle"] and len(v) == 3:
|
|
129
133
|
return Idle(struct.unpack_from("<H", v)[0], _name(IDLE, v[2]))
|
|
130
|
-
if tag == ITEM["slot"] and len(v) >=
|
|
131
|
-
n, wire_fn, swdio, swclk, attach, retry_s, mech, name_len = struct.unpack_from("<
|
|
132
|
-
name = v[
|
|
133
|
-
rest = v[
|
|
134
|
+
if tag == ITEM["slot"] and len(v) >= 18:
|
|
135
|
+
n, wire_fn, swdio, swclk, attach, retry_s, max_speed, idle, mech, name_len = struct.unpack_from("<BHHHBHIBBB", v)
|
|
136
|
+
name = v[17:17 + name_len].decode("ascii", "replace")
|
|
137
|
+
rest = v[17 + name_len:]
|
|
134
138
|
lock = None
|
|
135
139
|
if rest and rest[0]:
|
|
136
140
|
half = (len(rest) - 1) // 2
|
|
137
141
|
lock = (rest[0], rest[1:1 + half], rest[1 + half:])
|
|
138
|
-
return Slot(n, wire_fn, (swdio, swclk), name, _name(ATTACH, attach), retry_s, _name(MECHANISM, mech), lock
|
|
142
|
+
return Slot(n, wire_fn, (swdio, swclk), name, _name(ATTACH, attach), retry_s, _name(MECHANISM, mech), lock,
|
|
143
|
+
max_speed, _name(IDLE_CLOCK, idle))
|
|
139
144
|
if tag == ITEM["bind"] and len(v) >= 4:
|
|
140
145
|
port, mode, selected, n = struct.unpack_from("<BBBB", v)
|
|
141
146
|
streams = [(_name(STREAM, v[4 + 3 * k]), struct.unpack_from("<H", v, 5 + 3 * k)[0]) for k in range(n)]
|
|
@@ -60,6 +60,7 @@ WIRES = ("oep.wire.rvswd", "oep.wire.swio")
|
|
|
60
60
|
OWNER = reg.CORE.tlv["open"]["owner"]
|
|
61
61
|
SLOT_NAME = re.compile(r"[a-z0-9_-]{1,32}")
|
|
62
62
|
NO_SLOT, NEVER = 0xFF, 0xFFFFFFFF
|
|
63
|
+
PIN_ROLE_RESET = reg.WIRE_RVSWD.enum["pin_role"]["reset"] # the channels attach_under_reset may take
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
class Reject(Exception):
|
|
@@ -180,6 +181,7 @@ class Connection:
|
|
|
180
181
|
speed: int = 4_000_000
|
|
181
182
|
tid: int | None = None
|
|
182
183
|
users: set = field(default_factory=set) # "host" and/or ("slot", n)
|
|
184
|
+
idle_clock: int = 0 # rvswd: SWCLK while the line rests, 0 high / 1 low
|
|
183
185
|
|
|
184
186
|
|
|
185
187
|
@dataclass(frozen=True)
|
|
@@ -189,6 +191,8 @@ class Slot:
|
|
|
189
191
|
pair: tuple[int, int]
|
|
190
192
|
attach: int
|
|
191
193
|
retry_s: int
|
|
194
|
+
max_speed: int # 0: no ceiling (oep-if-debug §3: the target's, the host's to set)
|
|
195
|
+
idle_clock: int
|
|
192
196
|
mechanism: int
|
|
193
197
|
name: str
|
|
194
198
|
lock: tuple[int, bytes, bytes] | None # (scheme, mask, value)
|
|
@@ -249,9 +253,13 @@ class Endpoint:
|
|
|
249
253
|
self.serial_ports = {i for i, k in enumerate(self.transports) if k in fake.SERIAL_KINDS}
|
|
250
254
|
self.pairs: dict[int, list[tuple[int, int]]] = {} # wire fn -> allowed (swdio, swclk), declared order
|
|
251
255
|
self.max_connections: dict[int, int] = {}
|
|
256
|
+
self.reset_channels: dict[int, set[int]] = {} # wire fn -> channels attach_under_reset may take (role 3)
|
|
252
257
|
for fn, name in self.names.items():
|
|
253
258
|
if name in WIRES:
|
|
254
259
|
self.pairs[fn] = [self._group_pair(name, t) for t in self.static[fn] if t[0] == catalog.CHANNEL_GROUP]
|
|
260
|
+
self.reset_channels[fn] = {
|
|
261
|
+
c for t in self.static[fn] if t[0] == catalog.ROLE_CHANNELS and t[2] == PIN_ROLE_RESET
|
|
262
|
+
for c in catalog.bitmap_to_channels(struct.unpack_from("<H", t, 3)[0], t[5:2 + t[1]])}
|
|
255
263
|
self.max_connections[fn] = next((t[2] for t in self.static[fn] if t[0] == fake.MAX_CONNECTIONS), 1)
|
|
256
264
|
self.targets: dict[tuple[int, tuple[int, int]], FakeTarget] = {
|
|
257
265
|
(fn, p): FakeTarget() for fn in sorted(self.pairs) for p in self.pairs[fn]}
|
|
@@ -575,17 +583,18 @@ class Endpoint:
|
|
|
575
583
|
got.append(struct.unpack_from("<HBH", value))
|
|
576
584
|
elif tag & m.TAG_CRITICAL:
|
|
577
585
|
return m.REJECTED, m.UNSUPPORTED, bytes([tag])
|
|
578
|
-
self._check_plan(got)
|
|
579
586
|
named = {fn for fn, _, _ in got}
|
|
587
|
+
if named & self.plan_from_config: # the settings' plan is the settings' (core §8)
|
|
588
|
+
raise Reject(m.UNAVAILABLE)
|
|
589
|
+
self._check_plan(got)
|
|
580
590
|
self.plan = {a for a in self.plan if a[0] not in named} | set(got)
|
|
581
|
-
self.plan_from_config -= named
|
|
582
591
|
return m.COMPLETED, m.SUCCESS, b""
|
|
583
592
|
if op == m.OP_PLAN_RELEASE: # n(u8) n x fn(u16); n = 0: every fn
|
|
584
593
|
n = t.take("B")
|
|
585
594
|
fns = {t.take("H") for _ in range(n)}
|
|
586
595
|
t.tail()
|
|
587
|
-
for fn in {a[0] for a in self.plan if not fns or a[0] in fns}:
|
|
588
|
-
self._drop_plan(fn)
|
|
596
|
+
for fn in {a[0] for a in self.plan if not fns or a[0] in fns} - self.plan_from_config:
|
|
597
|
+
self._drop_plan(fn) # the settings' plans stay, n = 0 too (core §8)
|
|
589
598
|
return m.COMPLETED, m.SUCCESS, b""
|
|
590
599
|
return m.REJECTED, m.UNKNOWN_OPERATION, b""
|
|
591
600
|
|
|
@@ -657,10 +666,14 @@ class Endpoint:
|
|
|
657
666
|
if method > 1:
|
|
658
667
|
raise Reject(m.UNSUPPORTED)
|
|
659
668
|
else:
|
|
660
|
-
channel, _hold = t.take("HH")
|
|
661
|
-
if channel not in (
|
|
669
|
+
channel, _hold = t.take("HH") # no default reset line (oep-if-debug §3)
|
|
670
|
+
if channel not in self.reset_channels.get(fn, set()) or any(a[2] == channel for a in self.plan):
|
|
662
671
|
raise Reject(m.UNAVAILABLE)
|
|
663
|
-
|
|
672
|
+
rvswd = self.names[fn] == "oep.wire.rvswd"
|
|
673
|
+
got, ignored = t.tail({0x01, 0x03, 0x04} if rvswd else {0x01, 0x03})
|
|
674
|
+
if 0x04 in got and (len(got[0x04]) != 1 or got[0x04][0] > 1):
|
|
675
|
+
raise Reject(m.MALFORMED)
|
|
676
|
+
idle_clock = got[0x04][0] if 0x04 in got else 0
|
|
664
677
|
pair = self._pick_pair(fn, got)
|
|
665
678
|
tg = self.targets[(fn, pair)]
|
|
666
679
|
speed = min(4_000_000, struct.unpack("<I", got[0x01])[0]) if 0x01 in got else 4_000_000
|
|
@@ -676,6 +689,7 @@ class Endpoint:
|
|
|
676
689
|
flags |= 2
|
|
677
690
|
self.conns[cid].speed = min(self.conns[cid].speed, speed)
|
|
678
691
|
c = self.conns[cid]
|
|
692
|
+
c.idle_clock = idle_clock # an existing connection takes the new rest level
|
|
679
693
|
c.users.add("host")
|
|
680
694
|
for n, s in self.slots.items(): # a new connection for an evicted slot: a new cue
|
|
681
695
|
if s.wire_fn == fn and s.pair == pair:
|
|
@@ -1218,7 +1232,7 @@ class Endpoint:
|
|
|
1218
1232
|
|
|
1219
1233
|
def _parse_slot(self, v: bytes) -> Slot:
|
|
1220
1234
|
t = Take(v)
|
|
1221
|
-
n, wire_fn, swdio, swclk, attach, retry_s, mech, name_len = t.take("
|
|
1235
|
+
n, wire_fn, swdio, swclk, attach, retry_s, max_speed, idle_clock, mech, name_len = t.take("BHHHBHIBBB")
|
|
1222
1236
|
name = t.bytes(name_len)
|
|
1223
1237
|
scheme = t.take("B")
|
|
1224
1238
|
rest = v[t.at:]
|
|
@@ -1226,6 +1240,8 @@ class Endpoint:
|
|
|
1226
1240
|
raise Reject(m.MALFORMED)
|
|
1227
1241
|
if retry_s and attach != SLOT_ATTACH["at_boot"]:
|
|
1228
1242
|
raise Reject(m.MALFORMED)
|
|
1243
|
+
if idle_clock > 1 or (idle_clock and self.names.get(wire_fn) != "oep.wire.rvswd"):
|
|
1244
|
+
raise Reject(m.MALFORMED)
|
|
1229
1245
|
if not SLOT_NAME.fullmatch(name.decode("ascii", "replace")):
|
|
1230
1246
|
raise Reject(m.MALFORMED)
|
|
1231
1247
|
if self.names.get(wire_fn) not in WIRES or (swdio, swclk) not in self.pairs.get(wire_fn, []):
|
|
@@ -1239,7 +1255,7 @@ class Endpoint:
|
|
|
1239
1255
|
lock = (scheme, rest[:len(rest) // 2], rest[len(rest) // 2:])
|
|
1240
1256
|
elif rest:
|
|
1241
1257
|
raise Reject(m.MALFORMED)
|
|
1242
|
-
return Slot(n, wire_fn, (swdio, swclk), attach, retry_s, mech, name.decode(), lock)
|
|
1258
|
+
return Slot(n, wire_fn, (swdio, swclk), attach, retry_s, max_speed, idle_clock, mech, name.decode(), lock)
|
|
1243
1259
|
|
|
1244
1260
|
def _parse_bind(self, v: bytes, slots: dict[int, Slot]) -> Bind:
|
|
1245
1261
|
t = Take(v)
|
|
@@ -1291,7 +1307,9 @@ class Endpoint:
|
|
|
1291
1307
|
if not tg.present:
|
|
1292
1308
|
return
|
|
1293
1309
|
try:
|
|
1294
|
-
|
|
1310
|
+
speed = min(4_000_000, s.max_speed) if s.max_speed else 4_000_000 # the slot's line settings
|
|
1311
|
+
cid = self._seat(s.wire_fn, s.pair, tg, speed, evict=False) # automatic: never evicts
|
|
1312
|
+
self.conns[cid].idle_clock = s.idle_clock
|
|
1295
1313
|
except Reject:
|
|
1296
1314
|
return
|
|
1297
1315
|
tg.havereset = False
|
|
@@ -185,8 +185,8 @@ def esp32_v003() -> FakeProbe:
|
|
|
185
185
|
_core("3.0.0", "esp32-d0wd", bytes.fromhex("0070070d9394"), 40, reserved, f"{NS}.esp32-v003",
|
|
186
186
|
{16: "SWIO", 23: "NRST", 22: "DUT TX", 21: "DUT RX"},
|
|
187
187
|
_transports([(TRANSPORT["uart_bridge"], 0xFF)])),
|
|
188
|
-
Offered(1, 1, "oep.wire.swio", (catalog.channel_group(1, [(1, 16)]), catalog.
|
|
189
|
-
catalog.u8(MAX_CONNECTIONS, 1))),
|
|
188
|
+
Offered(1, 1, "oep.wire.swio", (catalog.channel_group(1, [(1, 16)]), catalog.role_channels(3, [23]),
|
|
189
|
+
catalog.u8(IMPLEMENTATION, 1), catalog.u8(MAX_CONNECTIONS, 1))),
|
|
190
190
|
Offered(2, 1, "oep.target.riscv-dm", (catalog.u32(FEATURES, 0b0111), catalog.u8(IMPLEMENTATION, 1),
|
|
191
191
|
catalog.u16(MAX_LENGTH, 40))),
|
|
192
192
|
Offered(3, 1, "oep.target.console", (catalog.tlv(MECHANISMS, bytes([0, 1, 2])),)),
|
|
@@ -77,8 +77,8 @@ def build(a: argparse.Namespace) -> endpoint.Endpoint:
|
|
|
77
77
|
swdio, swclk = ep.pairs[wire][n]
|
|
78
78
|
mech = 2 if 2 in ep.mechanisms else min(ep.mechanisms)
|
|
79
79
|
raw = name.encode()
|
|
80
|
-
value = struct.pack("<
|
|
81
|
-
mech, len(raw)) + raw + b"\x00"
|
|
80
|
+
value = struct.pack("<BHHHBHIBBB", n, wire, swdio, swclk, reg.PROBE_CONFIG.enum["slot_attach"]["at_boot"], 1,
|
|
81
|
+
0, 0, mech, len(raw)) + raw + b"\x00" # no speed ceiling, rests high, no lock
|
|
82
82
|
items.append(bytes([_ITEM["slot"], len(value)]) + value)
|
|
83
83
|
if a.bind:
|
|
84
84
|
streams = b"".join(struct.pack("<BH", reg.PROBE_CONFIG.enum["bind_stream"]["slot_console"], n)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from types import SimpleNamespace as _NS
|
|
4
4
|
|
|
5
|
-
REGISTRY_HASH = "
|
|
5
|
+
REGISTRY_HASH = "049bd156b4775990"
|
|
6
6
|
SCHEMA = 1
|
|
7
7
|
PROTOCOL_REVISION = 1
|
|
8
8
|
CONFIRM_REQUEST_MAGIC = 'OEP?'
|
|
@@ -22,13 +22,13 @@ TIMING = {"resync_quiet_ms": 0x32, "probe_frame_gap_ms": 0xC8, "host_frame_pause
|
|
|
22
22
|
|
|
23
23
|
INTERFACES = {}
|
|
24
24
|
CORE = _NS(name="oep.core", revision=1, op={"confirm": 0x01, "list": 0x02, "describe": 0x03, "plan_apply": 0x04, "plan_release": 0x05, "open": 0x10, "end": 0x11, "keepalive": 0x12, "lock_state": 0x13, "subscribe": 0x30, "unsubscribe": 0x32, "link_source": 0x40, "link_sink": 0x41}, lock_free={0x01, 0x02, 0x03, 0x10, 0x13, 0x40, 0x41},
|
|
25
|
-
closed_tail={0x40}, tlv={"plan_apply": {"role_assignment": 0x90}, "open": {"owner": 0x01}, "lock_state_answer": {"owner": 0x01}, "locked_payload": {"owner": 0x01}, "describe": {"firmware": 0x40, "model": 0x41, "unit_id": 0x42, "channels": 0x43, "reserved": 0x44, "profile": 0x45, "label": 0x46, "resets_on_open": 0x47, "transport": 0x49, "oep_pid": 0x4A}}, event={"heartbeat": 0x01}, enum={"transport_kind": {"uart_bridge": 0x01, "usb_cdc": 0x02, "usb_serial_jtag": 0x03, "vendor_bulk": 0x04, "hid": 0x05, "tcp": 0x06}}, own={})
|
|
25
|
+
closed_tail={0x40}, tlv={"plan_apply": {"role_assignment": 0x90}, "open": {"owner": 0x01}, "lock_state_answer": {"owner": 0x01}, "locked_payload": {"owner": 0x01}, "describe": {"firmware": 0x40, "model": 0x41, "unit_id": 0x42, "channels": 0x43, "reserved": 0x44, "profile": 0x45, "label": 0x46, "resets_on_open": 0x47, "transport": 0x49, "oep_pid": 0x4A, "plan_roles": 0x4B}}, event={"heartbeat": 0x01}, enum={"transport_kind": {"uart_bridge": 0x01, "usb_cdc": 0x02, "usb_serial_jtag": 0x03, "vendor_bulk": 0x04, "hid": 0x05, "tcp": 0x06}}, own={})
|
|
26
26
|
INTERFACES["oep.core"] = CORE
|
|
27
27
|
WIRE_RVSWD = _NS(name="oep.wire.rvswd", revision=1, op={"scan": 0x01, "attach": 0x02, "detach": 0x03, "attach_under_reset": 0x04, "connections": 0x05}, lock_free={0x05},
|
|
28
|
-
closed_tail={}, tlv={"describe": {"max_connections": 0x40}, "detach": {"force": 0x01}, "attach": {"max_speed": 0x01, "pins": 0x03}, "attach_under_reset": {"max_speed": 0x01, "pins": 0x03}, "attach_answer": {"target_id": 0x10}}, event={}, enum={"connection_users": {"host_session": 0x01, "slot": 0x02}, "target_id_scheme": {"wch_dmi_7f": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01, "swclk": 0x02}}, own={})
|
|
28
|
+
closed_tail={}, tlv={"describe": {"max_connections": 0x40}, "detach": {"force": 0x01}, "attach": {"max_speed": 0x01, "pins": 0x03, "idle_clock": 0x04}, "attach_under_reset": {"max_speed": 0x01, "pins": 0x03, "idle_clock": 0x04}, "attach_answer": {"target_id": 0x10}}, event={}, enum={"connection_users": {"host_session": 0x01, "slot": 0x02}, "target_id_scheme": {"wch_dmi_7f": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01, "swclk": 0x02, "reset": 0x03}, "idle_clock": {"high": 0x00, "low": 0x01}}, own={})
|
|
29
29
|
INTERFACES["oep.wire.rvswd"] = WIRE_RVSWD
|
|
30
30
|
WIRE_SWIO = _NS(name="oep.wire.swio", revision=1, op={"scan": 0x01, "attach": 0x02, "detach": 0x03, "attach_under_reset": 0x04, "connections": 0x05}, lock_free={0x05},
|
|
31
|
-
closed_tail={}, tlv={"describe": {"max_connections": 0x40}, "detach": {"force": 0x01}, "attach": {"max_speed": 0x01, "pins": 0x03}, "attach_under_reset": {"max_speed": 0x01, "pins": 0x03}, "attach_answer": {"target_id": 0x10}}, event={}, enum={"connection_users": {"host_session": 0x01, "slot": 0x02}, "target_id_scheme": {"wch_dmi_7f": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01}}, own={})
|
|
31
|
+
closed_tail={}, tlv={"describe": {"max_connections": 0x40}, "detach": {"force": 0x01}, "attach": {"max_speed": 0x01, "pins": 0x03}, "attach_under_reset": {"max_speed": 0x01, "pins": 0x03}, "attach_answer": {"target_id": 0x10}}, event={}, enum={"connection_users": {"host_session": 0x01, "slot": 0x02}, "target_id_scheme": {"wch_dmi_7f": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01, "reset": 0x03}}, own={})
|
|
32
32
|
INTERFACES["oep.wire.swio"] = WIRE_SWIO
|
|
33
33
|
WIRE_SWD = _NS(name="oep.wire.swd", revision=1, op={"scan": 0x01, "attach": 0x02, "detach": 0x03, "connections": 0x05}, lock_free={0x05},
|
|
34
34
|
closed_tail={}, tlv={"describe": {"max_connections": 0x40}, "detach": {"force": 0x01}, "attach": {"max_speed": 0x01, "targetsel": 0x02, "pins": 0x03}}, event={}, enum={"connection_users": {"host_session": 0x01, "slot": 0x02}, "pin_role": {"swdio": 0x01, "swclk": 0x02}}, own={})
|
|
@@ -14,8 +14,8 @@ import struct
|
|
|
14
14
|
import time
|
|
15
15
|
from dataclasses import dataclass, field
|
|
16
16
|
|
|
17
|
-
from . import host as h, message as m, registry as reg
|
|
18
|
-
from .core import Interface
|
|
17
|
+
from . import catalog, host as h, message as m, registry as reg
|
|
18
|
+
from .core import Interface, describe
|
|
19
19
|
from .fixture import Gpio
|
|
20
20
|
|
|
21
21
|
STATUS = reg.STATUS
|
|
@@ -109,7 +109,9 @@ class WireBase(Interface):
|
|
|
109
109
|
class Wire(WireBase):
|
|
110
110
|
"""oep.wire.rvswd / oep.wire.swio (CH32 debug links to a RISC-V debug module)."""
|
|
111
111
|
NAME = "oep.wire.rvswd"
|
|
112
|
-
|
|
112
|
+
ROLE_RESET = reg.WIRE_RVSWD.enum["pin_role"]["reset"]
|
|
113
|
+
TAG_IDLE_CLOCK = reg.WIRE_RVSWD.tlv["attach"]["idle_clock"]
|
|
114
|
+
IDLE_CLOCK = reg.WIRE_RVSWD.enum["idle_clock"]
|
|
113
115
|
RUN, HALT = reg.WIRE_RVSWD.enum["attach_method"]["run"], reg.WIRE_RVSWD.enum["attach_method"]["halt"]
|
|
114
116
|
TAG_TARGET_ID = reg.WIRE_RVSWD.tlv["attach_answer"]["target_id"]
|
|
115
117
|
SCHEME_WCH_DMI_7F = reg.WIRE_RVSWD.enum["target_id_scheme"]["wch_dmi_7f"]
|
|
@@ -125,12 +127,28 @@ class Wire(WireBase):
|
|
|
125
127
|
v = tail.get(self.TAG_TARGET_ID)
|
|
126
128
|
self.target_id = (v[0], bytes(v[1:])) if v else None
|
|
127
129
|
|
|
128
|
-
def
|
|
130
|
+
def _idle_tlv(self, idle_clock: str | None) -> bytes:
|
|
131
|
+
# rvswd only, critical: a probe that cannot rest the line that way must refuse (oep-if-debug §3)
|
|
132
|
+
return b"" if idle_clock is None else m.tlv(self.TAG_IDLE_CLOCK, bytes([self.IDLE_CLOCK[idle_clock]]), critical=True)
|
|
133
|
+
|
|
134
|
+
def reset_channels(self) -> list[int]:
|
|
135
|
+
"""The channels attach_under_reset may take (describe role_channels, role reset). There is no default reset
|
|
136
|
+
line: the host names one every time (oep-if-debug §3)."""
|
|
137
|
+
out = set()
|
|
138
|
+
for tag, v in describe(self.host, self.fn):
|
|
139
|
+
if tag & ~m.TAG_CRITICAL == catalog.ROLE_CHANNELS and v[0] == self.ROLE_RESET:
|
|
140
|
+
out.update(catalog.bitmap_to_channels(struct.unpack_from("<H", v, 1)[0], v[3:]))
|
|
141
|
+
return sorted(out)
|
|
142
|
+
|
|
143
|
+
def attach(self, halt: bool = True, max_speed: int | None = None, pins: tuple[int, int] | None = None,
|
|
144
|
+
idle_clock: str | None = None) -> tuple[int, int]:
|
|
129
145
|
"""-> (connection, DMSTATUS). Attaching an attached wire returns its connection as it is (self.existing).
|
|
130
146
|
self.had_reset: a pending havereset was acknowledged first (a V00x's DMSTATUS halt / run bits stay frozen
|
|
131
147
|
until then); self.speed_hz: the speed the probe chose; max_speed: a ceiling the probe must keep (critical);
|
|
132
|
-
|
|
133
|
-
|
|
148
|
+
idle_clock: "high" / "low", how rvswd rests SWCLK (critical). Both are the target's, known by the host
|
|
149
|
+
(oep-if-debug §3). self.target_id: (scheme, value) of the target's identity when the probe could read one."""
|
|
150
|
+
body = (bytes([self.HALT if halt else self.RUN]) + self._speed_tlv(max_speed) + self._pins_tlv(pins)
|
|
151
|
+
+ self._idle_tlv(idle_clock))
|
|
134
152
|
rd = m.Reader(self._call(self.ATTACH, body).payload)
|
|
135
153
|
conn, status, flags, self.speed_hz = rd.take("HIBI")
|
|
136
154
|
self.had_reset, self.existing = bool(flags & 1), bool(flags & 2)
|
|
@@ -139,12 +157,13 @@ class Wire(WireBase):
|
|
|
139
157
|
self._take_target_id(tail)
|
|
140
158
|
return conn, status
|
|
141
159
|
|
|
142
|
-
def attach_under_reset(self, channel: int
|
|
143
|
-
|
|
144
|
-
"""Hold the target in reset through `channel` (
|
|
145
|
-
halt it at once - the way back from firmware that turns the
|
|
146
|
-
|
|
147
|
-
|
|
160
|
+
def attach_under_reset(self, channel: int, hold_ms: int = 20, max_speed: int | None = None,
|
|
161
|
+
pins: tuple[int, int] | None = None, idle_clock: str | None = None) -> tuple[int, int]:
|
|
162
|
+
"""Hold the target in reset through `channel` (always named: there is no default reset line; the probe
|
|
163
|
+
allows reset_channels()), attach, release and halt it at once - the way back from firmware that turns the
|
|
164
|
+
debug pins into GPIOs. -> (connection, dpc)"""
|
|
165
|
+
body = (struct.pack("<HH", channel, hold_ms) + self._speed_tlv(max_speed) + self._pins_tlv(pins)
|
|
166
|
+
+ self._idle_tlv(idle_clock))
|
|
148
167
|
rd = m.Reader(self._call(self.ATTACH_UNDER_RESET, body).payload)
|
|
149
168
|
conn, dpc, self.speed_hz = rd.take("HII")
|
|
150
169
|
self._take_target_id(rd.tail())
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import struct
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from oep_client import __main__ as cli, config, endpoint, fake, host as h, message as m
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class Clock:
|
|
@@ -108,3 +110,44 @@ def test_a_plan_refused_for_a_saved_plan_names_the_holder():
|
|
|
108
110
|
with pytest.raises(core.PinsTaken) as e:
|
|
109
111
|
core.plan_apply(hst, [(4, 1, 21)]) # gpio wants 21
|
|
110
112
|
assert e.value.holders == [(21, "the saved plan of fn 5 (role 2)")] and "fn 5" in str(e.value)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_a_settings_plan_is_not_the_sessions():
|
|
116
|
+
"""core §8: plan_release leaves a plan the settings put in (n = 0 too); plan_apply naming its fn is refused."""
|
|
117
|
+
ep, hst = open_bench()
|
|
118
|
+
config.ProbeConfig(hst).set([config.Plan(5, 1, 20), config.Plan(5, 2, 21)])
|
|
119
|
+
assert 5 in ep.plan_from_config
|
|
120
|
+
with pytest.raises(h.Rejected) as e:
|
|
121
|
+
hst.call(m.CORE_FN, m.OP_PLAN_APPLY, m.tlv(0x90, struct.pack("<HBH", 5, 1, 22)))
|
|
122
|
+
assert e.value.result.detail == m.UNAVAILABLE
|
|
123
|
+
hst.call(m.CORE_FN, m.OP_PLAN_RELEASE, b"\x00")
|
|
124
|
+
assert {(5, 1, 20), (5, 2, 21)} <= ep.plan and 5 in ep.plan_from_config
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_reset_channels_and_a_named_reset_line():
|
|
128
|
+
"""oep-if-debug §3: the host reads the reset channels the probe allows and names one; there is no default."""
|
|
129
|
+
from oep_client import target
|
|
130
|
+
ep = endpoint.Endpoint(fake.esp32_v003(), Clock())
|
|
131
|
+
hst = h.Host(lambda b: ep.handle(b, 0))
|
|
132
|
+
hst.open(3000)
|
|
133
|
+
wire = target.Wire(hst, "oep.wire.swio")
|
|
134
|
+
assert wire.reset_channels() == [23]
|
|
135
|
+
conn, _dpc = wire.attach_under_reset(23)
|
|
136
|
+
assert conn in ep.conns
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_slot_line_settings_from_the_command(capsys, monkeypatch):
|
|
140
|
+
ep, hst = open_bench()
|
|
141
|
+
|
|
142
|
+
class FakeLink:
|
|
143
|
+
def close(self):
|
|
144
|
+
pass
|
|
145
|
+
hst.link = FakeLink()
|
|
146
|
+
hst.end()
|
|
147
|
+
monkeypatch.setattr(cli.link, "open_host", lambda target: hst)
|
|
148
|
+
assert cli.main(["config", "slot", "x", "--name", "l103", "--pins", "2,3", "--attach", "at-boot", "--retry", "1",
|
|
149
|
+
"--max-speed", "1000000", "--idle-clock", "low"]) == 0
|
|
150
|
+
(slot,) = [i for i in config.ProbeConfig(hst).items() if isinstance(i, config.Slot)]
|
|
151
|
+
assert (slot.max_speed, slot.idle_clock) == (1_000_000, "low")
|
|
152
|
+
assert cli.main(["config", "show", "x"]) == 0
|
|
153
|
+
assert "max 1000000 Hz idle-low" in capsys.readouterr().out
|
|
@@ -49,10 +49,10 @@ class Host:
|
|
|
49
49
|
return r.payload
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def slot_item(n, wire, pair, attach=ATTACH["at_boot"], retry=1, mech=2, name=None, lock=None):
|
|
52
|
+
def slot_item(n, wire, pair, attach=ATTACH["at_boot"], retry=1, mech=2, name=None, lock=None, max_speed=0, idle=0):
|
|
53
53
|
raw = (name or f"s{n}").encode()
|
|
54
|
-
value = struct.pack("<
|
|
55
|
-
len(raw)) + raw
|
|
54
|
+
value = struct.pack("<BHHHBHIBBB", n, wire, *pair, attach, retry if attach == ATTACH["at_boot"] else 0, max_speed,
|
|
55
|
+
idle, mech, len(raw)) + raw
|
|
56
56
|
value += b"\x00" if lock is None else bytes([1]) + lock[0] + lock[1]
|
|
57
57
|
return m.tlv(ITEM["slot"], value)
|
|
58
58
|
|
|
@@ -198,7 +198,7 @@ def test_too_many_at_boot_slots_are_refused():
|
|
|
198
198
|
def test_slot_names_are_url_safe(name):
|
|
199
199
|
ep, h = bench()
|
|
200
200
|
assert h.raw(6, 0x02, slot_item(0, 1, ep.pairs[1][0], name=name or None) if name else
|
|
201
|
-
m.tlv(ITEM["slot"], struct.pack("<
|
|
201
|
+
m.tlv(ITEM["slot"], struct.pack("<BHHHBHIBBB", 0, 1, *ep.pairs[1][0], 1, 1, 0, 0, 2, 0) + b"\x00")).detail == m.MALFORMED
|
|
202
202
|
|
|
203
203
|
|
|
204
204
|
# ---- binds and the serial port ---------------------------------------------------------------------------
|
|
@@ -396,3 +396,38 @@ def test_read_from_a_last_mark_that_is_not_there_is_from_now():
|
|
|
396
396
|
reset = reg.TARGET_CONSOLE.enum["mark_kind"]["reset"]
|
|
397
397
|
got = h.raw(uart_fn, 0x02, struct.pack("<BQH", 3, reset, 64), session=False).payload
|
|
398
398
|
assert struct.unpack_from("<Q", got)[0] == 10 and got[9:] == b"" # no reset mark: from now, not the old bytes
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def test_no_default_reset_line_only_the_declared_channels():
|
|
402
|
+
"""oep-if-debug §3: attach_under_reset names its channel; the probe takes only role 3 (reset) channels, and not
|
|
403
|
+
one a plan holds."""
|
|
404
|
+
ep = endpoint.Endpoint(fake.esp32_v003(), Clock())
|
|
405
|
+
h = Host(ep)
|
|
406
|
+
h.open()
|
|
407
|
+
assert ep.reset_channels[1] == {23}
|
|
408
|
+
for channel in (0xFFFF, 22): # no default; 22 is not a reset line
|
|
409
|
+
assert h.raw(1, 0x04, struct.pack("<HH", channel, 20)).detail == m.UNAVAILABLE
|
|
410
|
+
h.ok(1, 0x04, struct.pack("<HH", 23, 20))
|
|
411
|
+
h.ok(0, m.OP_PLAN_APPLY, m.tlv(0x90, struct.pack("<HBH", 4, 1, 23))) # fixture.gpio takes NRST
|
|
412
|
+
assert h.raw(1, 0x04, struct.pack("<HH", 23, 20)).detail == m.UNAVAILABLE
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def test_idle_clock_is_rvswd_s_and_a_slot_carries_the_line_settings():
|
|
416
|
+
ep, h = bench()
|
|
417
|
+
pair = ep.pairs[1][0]
|
|
418
|
+
idle_low = m.tlv(0x04, b"\x01", critical=True)
|
|
419
|
+
h.ok(1, 0x02, b"\x01" + m.tlv(0x03, struct.pack("<HH", *pair), critical=True) + idle_low)
|
|
420
|
+
assert ep.conns[ep._conn_at(1, pair)].idle_clock == 1
|
|
421
|
+
assert h.raw(1, 0x02, b"\x01" + m.tlv(0x03, struct.pack("<HH", *pair), critical=True)
|
|
422
|
+
+ m.tlv(0x04, b"\x02", critical=True)).detail == m.MALFORMED
|
|
423
|
+
ep2, h2 = bench()
|
|
424
|
+
p2 = ep2.pairs[1][1]
|
|
425
|
+
h2.ok(6, 0x02, slot_item(0, 1, p2, name="l103", max_speed=1_000_000, idle=1))
|
|
426
|
+
c = ep2.conns[ep2._conn_at(1, p2)]
|
|
427
|
+
assert (c.speed, c.idle_clock) == (1_000_000, 1) # the probe's own attach uses the slot's settings
|
|
428
|
+
v003 = endpoint.Endpoint(fake.esp32_v003(), Clock())
|
|
429
|
+
hv = Host(v003)
|
|
430
|
+
hv.open()
|
|
431
|
+
assert hv.raw(9, 0x02, slot_item(0, 1, v003.pairs[1][0], idle=1)).detail == m.MALFORMED # swio has no idle_clock
|
|
432
|
+
r = hv.raw(1, 0x02, b"\x01" + idle_low)
|
|
433
|
+
assert r.resolution == m.REJECTED and r.detail == m.UNSUPPORTED # an unknown critical TLV on swio
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|