oep-client-python 0.0.1__py3-none-any.whl
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/__init__.py +5 -0
- oep_client/v1/__init__.py +1 -0
- oep_client/v1/__main__.py +40 -0
- oep_client/v1/arm.py +269 -0
- oep_client/v1/capture.py +365 -0
- oep_client/v1/catalog.py +181 -0
- oep_client/v1/ch32_flash.py +254 -0
- oep_client/v1/cobs.py +73 -0
- oep_client/v1/console.py +165 -0
- oep_client/v1/core.py +172 -0
- oep_client/v1/decode.py +65 -0
- oep_client/v1/dump.py +157 -0
- oep_client/v1/endpoint.py +1479 -0
- oep_client/v1/esp32_targets.py +137 -0
- oep_client/v1/fake.py +233 -0
- oep_client/v1/fake_serial.py +105 -0
- oep_client/v1/fake_serve.py +299 -0
- oep_client/v1/fixture.py +121 -0
- oep_client/v1/frames.py +91 -0
- oep_client/v1/hid_stream.py +257 -0
- oep_client/v1/host.py +321 -0
- oep_client/v1/interfaces.py +119 -0
- oep_client/v1/link.py +414 -0
- oep_client/v1/message.py +235 -0
- oep_client/v1/names.py +86 -0
- oep_client/v1/registry.py +59 -0
- oep_client/v1/riscv.py +430 -0
- oep_client/v1/rp2350.py +85 -0
- oep_client/v1/target.py +16 -0
- oep_client/v1/uiapduino.py +121 -0
- oep_client/v1/usb_stream.py +209 -0
- oep_client_python-0.0.1.dist-info/METADATA +106 -0
- oep_client_python-0.0.1.dist-info/RECORD +36 -0
- oep_client_python-0.0.1.dist-info/WHEEL +4 -0
- oep_client_python-0.0.1.dist-info/entry_points.txt +2 -0
- oep_client_python-0.0.1.dist-info/licenses/LICENSE +21 -0
oep_client/v1/message.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""v1 messages (oep-spec docs/oep-core.ja.md §2-§5): the v0 request/result headers plus the session flag,
|
|
2
|
+
and the §0 rules for what follows a payload's fixed part.
|
|
3
|
+
|
|
4
|
+
request : role(0x01) corr(u16) fn(u16) op(u8) payload
|
|
5
|
+
role(0x81) corr(u16) fn(u16) op(u8) session_id(u32) payload role bit 7 = session_id present
|
|
6
|
+
result : role(0x02) corr(u16) resolution(u8) detail(u8) payload
|
|
7
|
+
|
|
8
|
+
§0 tails: after a result's fixed part (and any counted list) come TLVs (tag u8, len u8, value); the host skips tags it
|
|
9
|
+
does not know and never rejects a longer result. A request may end with TLVs too; tag bit 7 = critical (the probe
|
|
10
|
+
honours it or answers rejected unsupported with the tag), and the probe lists the non-critical tags it ignored in a
|
|
11
|
+
result TLV 0x7F. Numbers come from `registry` (generated from oep-spec registry/oep-v1.toml).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import struct
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
|
|
19
|
+
from . import registry as reg
|
|
20
|
+
|
|
21
|
+
ROLE_REQUEST, ROLE_RESULT = reg.ROLES["request"], reg.ROLES["result"]
|
|
22
|
+
ROLE_EVENT, ROLE_DATA = reg.ROLES["event"], reg.ROLES["data"]
|
|
23
|
+
ROLE_SESSION = reg.ROLE_SESSION_FLAG
|
|
24
|
+
REQUEST_HEADER, RESULT_HEADER, SESSION_BYTES = 6, 5, 4
|
|
25
|
+
|
|
26
|
+
REJECTED, COMPLETED, ACCEPTED = (reg.RESOLUTIONS[k] for k in ("rejected", "completed", "accepted"))
|
|
27
|
+
SUCCESS, FAILED, PARTIAL = (reg.OUTCOMES[k] for k in ("success", "failed", "partial"))
|
|
28
|
+
|
|
29
|
+
_R = reg.REJECT_REASONS
|
|
30
|
+
UNKNOWN_FUNCTION = _R["unknown_function"]
|
|
31
|
+
UNKNOWN_OPERATION = _R["unknown_operation"]
|
|
32
|
+
MALFORMED = _R["malformed"]
|
|
33
|
+
UNAVAILABLE = _R["unavailable"]
|
|
34
|
+
BUSY = _R["busy"] # a long operation is running: answered at once
|
|
35
|
+
WINDOW_EXCEEDED = _R["window_exceeded"]
|
|
36
|
+
NO_SESSION = _R["no_session"] # lock free, but this is not the last session id: open again
|
|
37
|
+
LOCKED = _R["locked"] # another session holds the lock; payload = remaining ms (u32)
|
|
38
|
+
SESSION_REQUIRED = _R["session_required"] # a state-changing request came without a session id
|
|
39
|
+
NO_CONNECTION = _R["no_connection"] # the probe does not know the request's connection: attach again
|
|
40
|
+
UNSUPPORTED = _R["unsupported"] # a critical TLV (payload: its tag) or a fixed-part value it cannot handle
|
|
41
|
+
RESULT_LOST = _R["result_lost"] # a request sent again whose result the probe did not keep: read the state again
|
|
42
|
+
CORR_REUSED = _R["corr_reused"] # a request sent again with the same corr but another fn, op or payload
|
|
43
|
+
|
|
44
|
+
REJECT_NAMES = {v: k.replace("_", " ") for k, v in _R.items()}
|
|
45
|
+
REJECT_NAMES[MALFORMED] = "malformed payload"
|
|
46
|
+
|
|
47
|
+
TAG_CRITICAL, TAG_IGNORED, TAG_INVALID = reg.TAG_CRITICAL, reg.TAG_IGNORED, reg.TAG_INVALID
|
|
48
|
+
|
|
49
|
+
# core (fn 0) operations
|
|
50
|
+
CORE_FN = 0
|
|
51
|
+
_OP = reg.CORE.op
|
|
52
|
+
OP_CONFIRM, OP_LIST, OP_DESCRIBE = _OP["confirm"], _OP["list"], _OP["describe"]
|
|
53
|
+
OP_PLAN_APPLY, OP_PLAN_RELEASE = _OP["plan_apply"], _OP["plan_release"]
|
|
54
|
+
OP_OPEN, OP_END, OP_KEEPALIVE, OP_LOCK_STATE = _OP["open"], _OP["end"], _OP["keepalive"], _OP["lock_state"]
|
|
55
|
+
OP_SUBSCRIBE, OP_UNSUBSCRIBE = _OP["subscribe"], _OP["unsubscribe"]
|
|
56
|
+
OP_LINK_SOURCE, OP_LINK_SINK = _OP["link_source"], _OP["link_sink"]
|
|
57
|
+
|
|
58
|
+
CONFIRM_REQUEST = reg.CONFIRM_REQUEST_MAGIC.encode()
|
|
59
|
+
CONFIRM_RESULT = reg.CONFIRM_RESULT_MAGIC.encode()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class OepError(Exception):
|
|
63
|
+
"""Anything the probe or the link said no to."""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ProtocolError(OepError, ValueError):
|
|
67
|
+
"""A result that does not fit: wrong correlation, too short, a value this host does not know."""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ShortPayload(ProtocolError):
|
|
71
|
+
"""A payload shorter than its fixed part, or a truncated TLV (a broken result, §0)."""
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass(frozen=True)
|
|
75
|
+
class Request:
|
|
76
|
+
corr: int
|
|
77
|
+
fn: int
|
|
78
|
+
op: int
|
|
79
|
+
payload: bytes = b""
|
|
80
|
+
session: int | None = None
|
|
81
|
+
|
|
82
|
+
def pack(self) -> bytes:
|
|
83
|
+
if self.session is None:
|
|
84
|
+
return struct.pack("<BHHB", ROLE_REQUEST, self.corr, self.fn, self.op) + self.payload
|
|
85
|
+
return (struct.pack("<BHHBI", ROLE_REQUEST | ROLE_SESSION, self.corr, self.fn, self.op, self.session)
|
|
86
|
+
+ self.payload)
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def unpack(cls, data: bytes) -> Request:
|
|
90
|
+
if len(data) < REQUEST_HEADER:
|
|
91
|
+
raise ValueError("request shorter than its header")
|
|
92
|
+
role, corr, fn, op = struct.unpack_from("<BHHB", data)
|
|
93
|
+
if role & ~ROLE_SESSION != ROLE_REQUEST:
|
|
94
|
+
raise ValueError(f"not a request: role 0x{role:02x}")
|
|
95
|
+
if role & ROLE_SESSION:
|
|
96
|
+
if len(data) < REQUEST_HEADER + SESSION_BYTES:
|
|
97
|
+
raise ValueError("session flag set but no session id")
|
|
98
|
+
(session,) = struct.unpack_from("<I", data, REQUEST_HEADER)
|
|
99
|
+
return cls(corr, fn, op, data[REQUEST_HEADER + SESSION_BYTES:], session)
|
|
100
|
+
return cls(corr, fn, op, data[REQUEST_HEADER:], None)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass(frozen=True)
|
|
104
|
+
class Result:
|
|
105
|
+
corr: int
|
|
106
|
+
resolution: int
|
|
107
|
+
detail: int
|
|
108
|
+
payload: bytes = b""
|
|
109
|
+
|
|
110
|
+
def pack(self) -> bytes:
|
|
111
|
+
return struct.pack("<BHBB", ROLE_RESULT, self.corr, self.resolution, self.detail) + self.payload
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def unpack(cls, data: bytes) -> Result:
|
|
115
|
+
if len(data) < RESULT_HEADER:
|
|
116
|
+
raise ShortPayload("result shorter than its header")
|
|
117
|
+
role, corr, resolution, detail = struct.unpack_from("<BHBB", data)
|
|
118
|
+
if role != ROLE_RESULT:
|
|
119
|
+
raise ValueError(f"not a result: role 0x{role:02x}")
|
|
120
|
+
return cls(corr, resolution, detail, data[RESULT_HEADER:])
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def succeeded(self) -> bool:
|
|
124
|
+
return self.resolution == COMPLETED and self.detail == SUCCESS
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def ran(self) -> bool:
|
|
128
|
+
"""Completed with a known outcome (success, failed, partial): the payload has the op's result shape. An unknown
|
|
129
|
+
resolution or outcome is a failure whose payload means nothing (§0)."""
|
|
130
|
+
return self.resolution == COMPLETED and self.detail in (SUCCESS, FAILED, PARTIAL)
|
|
131
|
+
|
|
132
|
+
def describe(self) -> str:
|
|
133
|
+
if self.resolution == REJECTED:
|
|
134
|
+
return f"rejected: {REJECT_NAMES.get(self.detail, f'reason 0x{self.detail:02x}')}"
|
|
135
|
+
if self.resolution == ACCEPTED:
|
|
136
|
+
return "accepted"
|
|
137
|
+
if self.resolution != COMPLETED:
|
|
138
|
+
return f"unknown resolution 0x{self.resolution:02x}"
|
|
139
|
+
return {SUCCESS: "completed", FAILED: "failed", PARTIAL: "partial"}.get(self.detail,
|
|
140
|
+
f"unknown outcome 0x{self.detail:02x}")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# ---- §0 TLV tails ------------------------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
def tlv(tag: int, value: bytes, critical: bool = False) -> bytes:
|
|
146
|
+
"""One TLV; `critical` sets tag bit 7 (a request argument the probe must honour or refuse)."""
|
|
147
|
+
if len(value) > 255:
|
|
148
|
+
raise ValueError(f"TLV 0x{tag:02x}: value of {len(value)} bytes does not fit")
|
|
149
|
+
if tag & 0x7F == TAG_IGNORED:
|
|
150
|
+
raise ValueError("tag 0x7F is reserved for the ignored list")
|
|
151
|
+
return bytes((tag | (TAG_CRITICAL if critical else 0), len(value))) + value
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def split_tlvs(data: bytes) -> list[tuple[int, bytes]]:
|
|
155
|
+
"""TLVs in order. A truncated TLV raises ShortPayload (the result is broken)."""
|
|
156
|
+
pos, out = 0, []
|
|
157
|
+
while pos < len(data):
|
|
158
|
+
if pos + 2 > len(data):
|
|
159
|
+
raise ShortPayload("TLV: truncated header")
|
|
160
|
+
tag, n = data[pos], data[pos + 1]
|
|
161
|
+
if pos + 2 + n > len(data):
|
|
162
|
+
raise ShortPayload(f"TLV 0x{tag:02x}: truncated value")
|
|
163
|
+
out.append((tag, data[pos + 2:pos + 2 + n]))
|
|
164
|
+
pos += 2 + n
|
|
165
|
+
return out
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@dataclass
|
|
169
|
+
class Tail:
|
|
170
|
+
"""The TLVs after a result's known part: `tlvs` in order (unknown tags included, for whoever knows them), and
|
|
171
|
+
`ignored`, the non-critical request tags the probe said it ignored (TLV 0x7F)."""
|
|
172
|
+
tlvs: list[tuple[int, bytes]] = field(default_factory=list)
|
|
173
|
+
ignored: list[int] = field(default_factory=list)
|
|
174
|
+
|
|
175
|
+
def get(self, tag: int) -> bytes | None:
|
|
176
|
+
return next((v for t, v in self.tlvs if t == tag), None)
|
|
177
|
+
|
|
178
|
+
@classmethod
|
|
179
|
+
def parse(cls, data: bytes) -> Tail:
|
|
180
|
+
t = cls()
|
|
181
|
+
for tag, value in split_tlvs(data):
|
|
182
|
+
if tag == TAG_IGNORED:
|
|
183
|
+
t.ignored += list(value)
|
|
184
|
+
else:
|
|
185
|
+
t.tlvs.append((tag, value))
|
|
186
|
+
return t
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class Reader:
|
|
190
|
+
"""Reads a result payload's fixed part front to back; too short -> ShortPayload. `tail()` then reads the rest as
|
|
191
|
+
§0 TLVs; `rest()` takes the rest as bytes (a result ending with a list of unknown length: nothing may follow)."""
|
|
192
|
+
|
|
193
|
+
def __init__(self, payload: bytes):
|
|
194
|
+
self.data, self.at = payload, 0
|
|
195
|
+
|
|
196
|
+
def take(self, fmt: str):
|
|
197
|
+
size = struct.calcsize("<" + fmt)
|
|
198
|
+
if self.at + size > len(self.data):
|
|
199
|
+
raise ShortPayload(f"payload of {len(self.data)} bytes: needs {self.at + size} for its fixed part")
|
|
200
|
+
values = struct.unpack_from("<" + fmt, self.data, self.at)
|
|
201
|
+
self.at += size
|
|
202
|
+
return values if len(values) > 1 else values[0]
|
|
203
|
+
|
|
204
|
+
def u8(self) -> int:
|
|
205
|
+
return self.take("B")
|
|
206
|
+
|
|
207
|
+
def u16(self) -> int:
|
|
208
|
+
return self.take("H")
|
|
209
|
+
|
|
210
|
+
def u32(self) -> int:
|
|
211
|
+
return self.take("I")
|
|
212
|
+
|
|
213
|
+
def bytes(self, n: int) -> bytes:
|
|
214
|
+
if self.at + n > len(self.data):
|
|
215
|
+
raise ShortPayload(f"payload of {len(self.data)} bytes: needs {self.at + n}")
|
|
216
|
+
out = self.data[self.at:self.at + n]
|
|
217
|
+
self.at += n
|
|
218
|
+
return out
|
|
219
|
+
|
|
220
|
+
def words(self, n: int) -> list[int]:
|
|
221
|
+
return list(struct.unpack(f"<{n}I", self.bytes(4 * n)))
|
|
222
|
+
|
|
223
|
+
def rest(self) -> bytes:
|
|
224
|
+
out = self.data[self.at:]
|
|
225
|
+
self.at = len(self.data)
|
|
226
|
+
return out
|
|
227
|
+
|
|
228
|
+
def tail(self) -> Tail:
|
|
229
|
+
return Tail.parse(self.rest())
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def serial_diff(a: int, b: int, bits: int = 32) -> int:
|
|
233
|
+
"""a - b for values that wrap (positions u32, seq u16, µs u32): the difference as a signed number of `bits` (§0)."""
|
|
234
|
+
d = (a - b) & ((1 << bits) - 1)
|
|
235
|
+
return d - (1 << bits) if d >> (bits - 1) else d
|
oep_client/v1/names.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Interface names (oep-spec docs/capability-identification-comparison.ja.md, draft).
|
|
2
|
+
|
|
3
|
+
A name is dot-separated labels of lowercase ASCII letters, digits and '-', at most 48 bytes. The
|
|
4
|
+
first label says which kind of namespace it is:
|
|
5
|
+
|
|
6
|
+
oep. the OEP standard (reserved; `oep` is not a real top-level domain)
|
|
7
|
+
local. bench-only experiments, never published, no interoperability promise
|
|
8
|
+
uuid.<32 hex>. an author with no domain who still wants a unique namespace
|
|
9
|
+
<tld>.<domain>... reverse DNS of a domain the author owns, including hosting domains
|
|
10
|
+
such as io.github.<name> (GitHub gives <name>.github.io to one owner)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import re
|
|
16
|
+
|
|
17
|
+
MAX_NAME = 48
|
|
18
|
+
_LABEL = re.compile(r"[a-z0-9-]+\Z")
|
|
19
|
+
_TLD = re.compile(r"[a-z]{2,63}\Z")
|
|
20
|
+
_UUID = re.compile(r"[0-9a-f]{32}\Z")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class InvalidName(ValueError):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def kind(name: str) -> str:
|
|
28
|
+
"""'standard', 'local', 'uuid' or 'domain' - after validate()."""
|
|
29
|
+
first = name.split(".", 1)[0]
|
|
30
|
+
return {"oep": "standard", "local": "local", "uuid": "uuid"}.get(first, "domain")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def validate(name: str) -> str:
|
|
34
|
+
"""Return the name, or raise InvalidName saying which rule it breaks."""
|
|
35
|
+
raw = name.encode("ascii", "strict") if name.isascii() else None
|
|
36
|
+
if raw is None:
|
|
37
|
+
raise InvalidName(f"{name!r}: not ASCII")
|
|
38
|
+
if len(raw) > MAX_NAME:
|
|
39
|
+
raise InvalidName(f"{name!r}: {len(raw)} bytes, the limit is {MAX_NAME}")
|
|
40
|
+
labels = name.split(".")
|
|
41
|
+
if len(labels) < 2:
|
|
42
|
+
raise InvalidName(f"{name!r}: needs a namespace and at least one more label")
|
|
43
|
+
for label in labels:
|
|
44
|
+
if not _LABEL.match(label):
|
|
45
|
+
raise InvalidName(f"{name!r}: label {label!r} is not [a-z0-9-]+")
|
|
46
|
+
first = labels[0]
|
|
47
|
+
if first == "uuid":
|
|
48
|
+
if len(labels) < 3 or not _UUID.match(labels[1]):
|
|
49
|
+
raise InvalidName(f"{name!r}: uuid. must be followed by 32 lowercase hex digits and a name")
|
|
50
|
+
elif first not in ("oep", "local"):
|
|
51
|
+
if not _TLD.match(first):
|
|
52
|
+
raise InvalidName(f"{name!r}: {first!r} is not a top-level domain (reverse DNS expected)")
|
|
53
|
+
if len(labels) < 3:
|
|
54
|
+
raise InvalidName(f"{name!r}: reverse DNS needs <tld>.<domain>.<name>")
|
|
55
|
+
return name
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Hosting services whose domain is often written wrongly as a top level. Whether a first label
|
|
59
|
+
# is a real top-level domain is not checked (that needs the IANA list); these are the common slips.
|
|
60
|
+
_HOSTING = {"github": "io.github", "gitlab": "io.gitlab", "codeberg": "page.codeberg",
|
|
61
|
+
"bitbucket": "io.bitbucket"}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def lint(name: str) -> list[str]:
|
|
65
|
+
"""Warnings for names that are well-formed but break the namespace rules in spirit."""
|
|
66
|
+
labels = name.split(".")
|
|
67
|
+
out = []
|
|
68
|
+
if labels[0] in _HOSTING:
|
|
69
|
+
out.append(f"{labels[0]!r} is not a top-level domain; a {labels[0]} account's namespace is "
|
|
70
|
+
f"{_HOSTING[labels[0]]}.{labels[1] if len(labels) > 1 else '<name>'}")
|
|
71
|
+
if labels[0] == "com" and len(labels) > 1 and labels[1] in _HOSTING:
|
|
72
|
+
out.append(f"com.{labels[1]} is not given to accounts; use {_HOSTING[labels[1]]}.<name>")
|
|
73
|
+
return out
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def matches(name: str, prefix: str, exact: bool) -> bool:
|
|
77
|
+
"""List filtering: an empty prefix matches everything; otherwise match on label boundaries.
|
|
78
|
+
|
|
79
|
+
oep.fixture.uart matches oep.fixture.uart and oep.fixture.uart.stream, not oep.fixture.uart2;
|
|
80
|
+
with exact, only oep.fixture.uart itself.
|
|
81
|
+
"""
|
|
82
|
+
if not prefix:
|
|
83
|
+
return not exact
|
|
84
|
+
if exact:
|
|
85
|
+
return name == prefix
|
|
86
|
+
return name == prefix or name.startswith(prefix + ".")
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Generated by oep-spec tools/oepgen1.py from registry/oep-v1.toml - do not edit."""
|
|
2
|
+
|
|
3
|
+
from types import SimpleNamespace as _NS
|
|
4
|
+
|
|
5
|
+
REGISTRY_HASH = "7ba704094c2898d2"
|
|
6
|
+
SCHEMA = 1
|
|
7
|
+
PROTOCOL_REVISION = 1
|
|
8
|
+
CONFIRM_REQUEST_MAGIC = 'OEP?'
|
|
9
|
+
CONFIRM_RESULT_MAGIC = 'OEP!'
|
|
10
|
+
ROLE_SESSION_FLAG = 0x80
|
|
11
|
+
TAG_CRITICAL = 0x80
|
|
12
|
+
TAG_IGNORED = 0x7F
|
|
13
|
+
TAG_INVALID = 0xFF
|
|
14
|
+
MIN_MAX_FRAME = 0x40
|
|
15
|
+
ROLES = {"request": 0x01, "result": 0x02, "event": 0x05, "data": 0x06}
|
|
16
|
+
RESOLUTIONS = {"rejected": 0x00, "completed": 0x01, "accepted": 0x02}
|
|
17
|
+
OUTCOMES = {"success": 0x00, "failed": 0x01, "partial": 0x02}
|
|
18
|
+
REJECT_REASONS = {"unknown_function": 0x01, "unknown_operation": 0x02, "malformed": 0x03, "unavailable": 0x04, "busy": 0x05, "window_exceeded": 0x06, "no_session": 0x07, "locked": 0x08, "session_required": 0x09, "no_connection": 0x0A, "unsupported": 0x0B, "result_lost": 0x0C, "corr_reused": 0x0D}
|
|
19
|
+
STATUS = {"ok": 0x00, "wait": 0x01, "line": 0x02, "fault": 0x03, "timeout": 0x04, "state": 0x05}
|
|
20
|
+
DESCRIBE_COMMON = {"role_channels": 0x01, "max_clock_hz": 0x02, "max_length": 0x03, "min_clock_hz": 0x05, "features": 0x06, "implementation": 0x07, "channel_group": 0x08}
|
|
21
|
+
TIMING = {"resync_quiet_ms": 0x32, "probe_frame_gap_ms": 0xC8, "host_frame_pause_max_ms": 0x64, "heartbeat_default_ms": 0x3E8}
|
|
22
|
+
|
|
23
|
+
INTERFACES = {}
|
|
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={})
|
|
26
|
+
INTERFACES["oep.core"] = CORE
|
|
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={})
|
|
29
|
+
INTERFACES["oep.wire.rvswd"] = WIRE_RVSWD
|
|
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={})
|
|
32
|
+
INTERFACES["oep.wire.swio"] = WIRE_SWIO
|
|
33
|
+
WIRE_SWD = _NS(name="oep.wire.swd", revision=1, op={"scan": 0x01, "attach": 0x02, "detach": 0x03, "connections": 0x05}, lock_free={0x05},
|
|
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={})
|
|
35
|
+
INTERFACES["oep.wire.swd"] = WIRE_SWD
|
|
36
|
+
TARGET_RISCV_DM = _NS(name="oep.target.riscv-dm", revision=1, op={"dmi": 0x01, "halt": 0x02, "resume": 0x03, "reset": 0x04, "read_block": 0x05, "write_block": 0x06, "run": 0x07, "step": 0x08}, lock_free={},
|
|
37
|
+
closed_tail={}, tlv={"describe": {"clobbers": 0x40}, "reset": {"method": 0x01}}, event={}, enum={"dmi_step": {"write": 0x01, "read": 0x02, "poll_reads": 0x03, "wait_us": 0x04, "poll_us": 0x05}, "reset_mode": {"run": 0x00, "run_verified": 0x01, "halt_at_reset": 0x02}, "reset_method": {"probe_default": 0x00, "ndmreset": 0x01, "system_reset": 0x02}}, own={})
|
|
38
|
+
INTERFACES["oep.target.riscv-dm"] = TARGET_RISCV_DM
|
|
39
|
+
TARGET_ARM_ADI = _NS(name="oep.target.arm-adi", revision=1, op={"transfer": 0x01, "read_block": 0x02, "write_block": 0x03}, lock_free={},
|
|
40
|
+
closed_tail={}, tlv={}, event={}, enum={}, own={})
|
|
41
|
+
INTERFACES["oep.target.arm-adi"] = TARGET_ARM_ADI
|
|
42
|
+
TARGET_CONSOLE = _NS(name="oep.target.console", revision=1, op={"open": 0x01, "read": 0x02, "marks": 0x03, "clear": 0x04, "mark": 0x05, "write": 0x06, "close": 0x07}, lock_free={0x02, 0x03},
|
|
43
|
+
closed_tail={0x02}, tlv={"describe": {"mechanisms": 0x40}}, event={}, enum={"mechanism": {"sdi": 0x00, "dmdata": 0x01, "dmseq": 0x02}, "read_from": {"position": 0x00, "oldest": 0x01, "now": 0x02, "last_mark": 0x03}, "mark_kind": {"reset": 0x01, "restart": 0x02, "attach": 0x03, "detach": 0x04, "lost": 0x05, "clear": 0x06, "host": 0x07, "link_lost": 0x08}}, own={})
|
|
44
|
+
INTERFACES["oep.target.console"] = TARGET_CONSOLE
|
|
45
|
+
FIXTURE_GPIO = _NS(name="oep.fixture.gpio", revision=1, op={"set": 0x01, "read": 0x02}, lock_free={0x02},
|
|
46
|
+
closed_tail={}, tlv={"describe": {"modes": 0x40}}, event={}, enum={"role": {"line": 0x01}, "mode": {"input": 0x00, "input_pullup": 0x01, "input_pulldown": 0x02, "output_low": 0x03, "output_high": 0x04, "open_drain_low": 0x05, "open_drain_release": 0x06, "input_pullup_pulldown": 0x07}}, own={})
|
|
47
|
+
INTERFACES["oep.fixture.gpio"] = FIXTURE_GPIO
|
|
48
|
+
FIXTURE_UART = _NS(name="oep.fixture.uart", revision=1, op={"configure": 0x01, "read": 0x02, "marks": 0x03, "clear": 0x04, "mark": 0x05, "write": 0x06}, lock_free={0x02, 0x03},
|
|
49
|
+
closed_tail={0x02}, tlv={"configure": {"format": 0x01}, "describe": {"formats": 0x40}}, event={}, enum={"role": {"rx": 0x01, "tx": 0x02}}, own={})
|
|
50
|
+
INTERFACES["oep.fixture.uart"] = FIXTURE_UART
|
|
51
|
+
FIXTURE_CAPTURE = _NS(name="oep.fixture.capture", revision=1, op={"configure": 0x01, "start": 0x02, "stop": 0x03, "force": 0x04, "status": 0x05, "read": 0x06, "segments": 0x07, "release": 0x08, "query": 0x09}, lock_free={0x05, 0x06, 0x07, 0x09},
|
|
52
|
+
closed_tail={0x06}, tlv={"configure": {"mode": 0x40, "rate": 0x42, "samples": 0x43, "segments": 0x44, "trigger": 0x45, "pretrigger": 0x46, "frontend": 0x47}, "configure_answer": {"actual_rate": 0x50, "layout": 0x51, "actual_samples": 0x52, "actual_segments": 0x53, "timing": 0x54, "scale": 0x55, "blocking_ms": 0x56, "skew": 0x57}, "describe": {"mode": 0x40, "rate_range": 0x41, "rate_list": 0x42, "rate_limit": 0x43, "channels": 0x44, "trigger": 0x45, "frontend": 0x46, "max_read": 0x47, "segment_ring": 0x48, "frontend_shared": 0x49}}, event={"segment": 0x01, "stopped": 0x02, "triggered": 0x03}, enum={"mode": {"one_shot": 0x01, "repeat": 0x02, "streaming": 0x03}, "trigger": {"immediate": 0x00, "level": 0x01, "edge": 0x02, "cross_up": 0x03, "cross_down": 0x04}, "state": {"unconfigured": 0x00, "configured": 0x01, "waiting": 0x02, "capturing": 0x03, "done": 0x04, "paused": 0x05, "error": 0x06}, "stopped_reason": {"complete": 0x00, "host": 0x01, "no_free_segment": 0x02, "error": 0x03}, "segment_flag": {"gap": 0x01, "short": 0x02, "slipped": 0x04}}, own={})
|
|
53
|
+
INTERFACES["oep.fixture.capture"] = FIXTURE_CAPTURE
|
|
54
|
+
FIXTURE_ANALOG = _NS(name="oep.fixture.analog", revision=1, op={"configure": 0x01, "start": 0x02, "stop": 0x03, "force": 0x04, "status": 0x05, "read": 0x06, "segments": 0x07, "release": 0x08, "query": 0x09}, lock_free={0x05, 0x06, 0x07, 0x09},
|
|
55
|
+
closed_tail={0x06}, tlv={"configure": {"mode": 0x40, "rate": 0x42, "samples": 0x43, "segments": 0x44, "trigger": 0x45, "pretrigger": 0x46, "frontend": 0x47}, "configure_answer": {"actual_rate": 0x50, "layout": 0x51, "actual_samples": 0x52, "actual_segments": 0x53, "timing": 0x54, "scale": 0x55, "blocking_ms": 0x56, "skew": 0x57}, "describe": {"mode": 0x40, "rate_range": 0x41, "rate_list": 0x42, "rate_limit": 0x43, "channels": 0x44, "trigger": 0x45, "frontend": 0x46, "max_read": 0x47, "segment_ring": 0x48, "frontend_shared": 0x49}}, event={"segment": 0x01, "stopped": 0x02, "triggered": 0x03}, enum={"mode": {"one_shot": 0x01, "repeat": 0x02, "streaming": 0x03}, "trigger": {"immediate": 0x00, "level": 0x01, "edge": 0x02, "cross_up": 0x03, "cross_down": 0x04}, "state": {"unconfigured": 0x00, "configured": 0x01, "waiting": 0x02, "capturing": 0x03, "done": 0x04, "paused": 0x05, "error": 0x06}, "stopped_reason": {"complete": 0x00, "host": 0x01, "no_free_segment": 0x02, "error": 0x03}, "segment_flag": {"gap": 0x01, "short": 0x02, "slipped": 0x04}}, own={})
|
|
56
|
+
INTERFACES["oep.fixture.analog"] = FIXTURE_ANALOG
|
|
57
|
+
PROBE_CONFIG = _NS(name="oep.probe.config", revision=1, op={"get": 0x01, "set": 0x02, "save": 0x03, "erase": 0x04}, lock_free={0x01},
|
|
58
|
+
closed_tail={}, tlv={"item": {"plan": 0x01, "label": 0x02, "idle": 0x03, "slot": 0x04, "bind": 0x05}, "describe": {"storage": 0x40, "items": 0x41, "slots_max": 0x42, "bind_modes": 0x43, "slot_state": 0x44, "bind_state": 0x45}}, event={}, enum={"idle_mode": {"hi_z": 0x00, "pull_up": 0x01, "pull_down": 0x02}, "slot_attach": {"host": 0x00, "at_boot": 0x01}, "slot_state": {"connected": 0x00, "absent": 0x01, "lock_mismatch": 0x02, "no_target_id": 0x03}, "bind_mode": {"last_reset": 0x00, "manual": 0x01, "mixed": 0x02}, "bind_stream": {"slot_console": 0x01, "fixture_uart": 0x02}, "bind_flow": {"idle": 0x00, "streaming": 0x01, "held": 0x02}, "storage_state": {"none": 0x00, "applied": 0x01, "unreadable": 0x02}}, own={})
|
|
59
|
+
INTERFACES["oep.probe.config"] = PROBE_CONFIG
|