From b538e07577a0d788e80bac53b673d5e649a832ef Mon Sep 17 00:00:00 2001 From: Allen Hill Date: Mon, 6 Jul 2026 16:53:13 -0700 Subject: [PATCH] Establish the Lua dissector action/device/etc tables as authoritative Co-Authored-By: Claude Opus 4.8 --- .githooks/pre-commit | 11 + CMakeLists.txt | 25 + scripts/packet-analysis/avclan_plugin.lua | 4 +- scripts/sync_avclan_enums.py | 541 ++++++++++++++++++++++ src/avclan/avclan.h | 27 +- src/avclan/cdchanger.cc | 20 +- src/avclan/device.hpp | 23 +- src/avclan/peripheral.hpp | 16 +- src/sniffer.cc | 2 +- 9 files changed, 632 insertions(+), 37 deletions(-) create mode 100755 .githooks/pre-commit create mode 100755 scripts/sync_avclan_enums.py diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..daab45f --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Pre-commit: keep the C++ Device/Action enums in sync with the Lua dissector +# (scripts/sync_avclan_enums.py in its default lint + drift-check mode). A +# non-zero exit blocks the commit; fix with `scripts/sync_avclan_enums.py --fix`. +# +# Installed by the top-level CMake configure step, which points core.hooksPath +# at this directory. Bypass a single commit with `git commit --no-verify`. +set -euo pipefail + +repo_root="$(git rev-parse --show-toplevel)" +exec python3 "$repo_root/scripts/sync_avclan_enums.py" diff --git a/CMakeLists.txt b/CMakeLists.txt index 1223195..d75ec18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,31 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() endif() +# Install the repository's git hooks (see .githooks/) by pointing core.hooksPath +# at them at configure time. +# Opt out with -DINSTALL_GIT_HOOKS=OFF; a custom core.hooksPath is left untouched, +# and `git commit --no-verify` bypasses a single commit. +option(INSTALL_GIT_HOOKS "Point git core.hooksPath at .githooks/" ON) +if(INSTALL_GIT_HOOKS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") + find_program(GIT_EXECUTABLE git) + if(GIT_EXECUTABLE) + execute_process( + COMMAND "${GIT_EXECUTABLE}" config --get core.hooksPath + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE _git_hooks_path + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET) + if(_git_hooks_path STREQUAL "" OR _git_hooks_path STREQUAL ".githooks") + execute_process( + COMMAND "${GIT_EXECUTABLE}" config core.hooksPath .githooks + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + message(STATUS "git hooks: core.hooksPath -> .githooks") + else() + message(STATUS "git hooks: core.hooksPath already '${_git_hooks_path}'; leaving as-is") + endif() + endif() +endif() + # Hardware target (port) selection. The chosen target// directory supplies # the port implementation sources, the per-target include path, and all # hardware-specific build configuration (compile defs/options, device-pack diff --git a/scripts/packet-analysis/avclan_plugin.lua b/scripts/packet-analysis/avclan_plugin.lua index 290cb27..0a9c7b1 100644 --- a/scripts/packet-analysis/avclan_plugin.lua +++ b/scripts/packet-analysis/avclan_plugin.lua @@ -219,7 +219,7 @@ local known_devices = { -- [0x02] = "COM_EXT", -- @GadgetNutt [0x11] = "COMMUNICATION v1", -- @GadgetNutt reads 0x11 as COM_MASTER [0x12] = "COMMUNICATION v2", - [0x21] = "SW", -- @GadgetNutt reads 0x21 as SW_AUDIO + [0x21] = "SW_AUDIO", -- @GadgetNutt name [0x23] = "SW_NAME", -- @GadgetNutt reads 0x23 as SW_SHIFT [0x24] = "SW_CONVERTING", [0x25] = "CMD_SW", -- @GadgetNutt reads 0x25 as SW @@ -244,7 +244,7 @@ local known_devices = { [0x5F] = "TRIP_INFO_DRAWING", [0x60] = "TUNER", [0x61] = "TAPE_DECK", - [0x62] = "CD", + [0x62] = "CD_SINGLE", [0x63] = "CD_CHANGER", -- [0x64] = "MD", -- @GadgetNutt (MiniDisc) -- [0x65] = "MD_CH", diff --git a/scripts/sync_avclan_enums.py b/scripts/sync_avclan_enums.py new file mode 100755 index 0000000..8d75374 --- /dev/null +++ b/scripts/sync_avclan_enums.py @@ -0,0 +1,541 @@ +#!/usr/bin/env python3 +"""Keep the C++ Device/Action enums in sync with the Lua dissector tables. + +The Wireshark dissector in scripts/packet-analysis/avclan_plugin.lua is the +authoritative source of truth for AVC-LAN device and action names. Its +`known_devices` / `known_actions` tables and the firmware's C++ enums +(`enum class Device` in src/avclan/device.hpp, `enum ... Action` in +src/avclan/avclan.h) encode the same numeric values and drift apart by hand. + +This tool reconciles them, matching entries *by value* so a value renamed in +the dissector is propagated into C++ (and to every reference under src/), not +just added/removed. + + Default (no flags) lint the dissector names AND report enum drift; write + nothing; exit non-zero if either has problems. This is + what the pre-commit hook runs. + --fix lint first (never fold an invalid name into C++), then + apply: rewrite the enum bodies and search-replace the + renamed members across src/. + --lint validate the dissector names only. + --verbose list every reference-site replacement made by --fix. + +Reconciliation rules (see the project plan for the full rationale): + + * Match by value. Names are compared with a case/separator-insensitive + canonical key, so a case-only difference (LIST_FUNCTIONS_REQ vs + List_Functions_Req) is NOT a change -- the existing C++ spelling is kept. + * A beyond-case difference is a rename: the C++ member is renamed to the + house-style form of the dissector name and all references are updated. + * Full mirror: every active dissector entry missing from C++ is added; if C++ + carries a value the dissector lacks, that is an error (reported, not + dropped). Commented-out ("pencil-in") Lua entries are ignored. + * New/renamed members follow each enum's house style: Device -> UPPER_SNAKE, + Action -> Title_Case (with a small acronym allowlist kept uppercase). + * New members are inserted at their position in the Lua table (adjacent to + their nearest Lua neighbour that already exists in C++). + +The dissector is expected to keep *approximately* valid identifiers: spaces and +punctuation are silently converted to single underscores. Only two hard rules +are enforced by --lint (a name must not start with a digit, and must be at +least 3 characters); the 3-char floor also keeps the reference search-replace +from colliding on 2-character tokens. + +Runs as a pre-commit hook via .githooks/pre-commit; that directory is wired up +by the top-level CMake configure step (core.hooksPath). `git commit --no-verify` +still bypasses it. +""" + +import argparse +import os +import re +import sys + +# ---- locations ------------------------------------------------------------- + +LUA_REL = os.path.join("scripts", "packet-analysis", "avclan_plugin.lua") +SRC_REL = "src" +SRC_EXTS = (".c", ".cc", ".h", ".hpp") + +# Enum specs are matched to a Lua table by NAME; addresses are auto-detected +# separately (no Address enum exists yet). `style` picks the identifier casing, +# `hexfmt` the literal format for freshly added members (matched per file). +ENUM_SPECS = [ + { + "name": "Action", + "path": os.path.join("src", "avclan", "avclan.h"), + "lua_table": "known_actions", + "style": "action", + "hexfmt": "0x%02x", + }, + { + "name": "Device", + "path": os.path.join("src", "avclan", "device.hpp"), + "lua_table": "known_devices", + "style": "device", + "hexfmt": "0x%02X", + }, +] + +# Tokens kept fully uppercase when Title-casing an Action name, matching the +# existing hand-written style (CD_Enable_Repeat, Report_TOC). Note LAN -> Lan. +ACTION_ACRONYMS = {"CD", "TOC"} + +# ---- name helpers ---------------------------------------------------------- + + +def sanitize(name): + """Convert an approximate identifier to a valid one: non-word chars become + underscores and runs of underscores collapse to one.""" + s = re.sub(r"[^0-9A-Za-z_]", "_", name) + s = re.sub(r"_+", "_", s) + return s.strip("_") + + +def canonical(name): + """Case/separator-insensitive key used to match names by identity.""" + return re.sub(r"[^0-9A-Za-z]", "", name).upper() + + +def style_device(name): + return sanitize(name).upper() + + +def style_action(name): + parts = [p for p in sanitize(name).split("_") if p] + out = [] + for p in parts: + if p.upper() in ACTION_ACRONYMS: + out.append(p.upper()) + else: + out.append(p[:1].upper() + p[1:].lower()) + return "_".join(out) + + +STYLERS = {"device": style_device, "action": style_action} + +# ---- parsing --------------------------------------------------------------- + +# An active Lua entry: ` [0x11] = "NAME",` with no leading `--`. The regex +# anchors `[` right after the indentation, so commented `-- [0x..]` lines and +# prose comment lines never match. The name is taken from inside the quotes, +# so trailing `-- ...` comments are ignored. +LUA_ENTRY_RE = re.compile(r'^\s*\[\s*0x([0-9A-Fa-f]+)\s*\]\s*=\s*"([^"]*)"') +LUA_TABLE_OPEN = "local %s = {" +LUA_TABLE_CLOSE_RE = re.compile(r"^\s*\}") + +# A C++ enum member: ` Name = 0x11,` (or decimal). `//`-commented members are +# skipped by the caller, so they count as absent. +CPP_MEMBER_RE = re.compile(r"^\s*([A-Za-z_]\w*)\s*=\s*(0x[0-9A-Fa-f]+|\d+)") + + +class LuaEntry: + def __init__(self, value, name, lineno): + self.value = value + self.name = name + self.lineno = lineno + + +class CppMember: + def __init__(self, value, name, idx): + self.value = value + self.name = name + self.idx = idx # 0-based index into the file's line list + + +def read_lines(path): + with open(path, "r") as f: + return f.readlines() + + +def parse_lua_table(lines, table_name): + """Return the active entries of a Lua table, in file order.""" + open_marker = LUA_TABLE_OPEN % table_name + start = None + for i, line in enumerate(lines): + if line.strip().startswith(open_marker): + start = i + break + if start is None: + return None + entries = [] + for j in range(start + 1, len(lines)): + line = lines[j] + if LUA_TABLE_CLOSE_RE.match(line): + break + if line.lstrip().startswith("--"): + continue + m = LUA_ENTRY_RE.match(line) + if m: + entries.append(LuaEntry(int(m.group(1), 16), m.group(2), j + 1)) + return entries + + +def find_enum_body(lines, enum_name): + """Locate `enum [macro] ... {` and its closing brace. Returns + (open_idx, close_idx) as line indices, or None.""" + open_re = re.compile(r"^\s*enum\b[^{;]*\b%s\b[^{;]*\{" % re.escape(enum_name)) + for i, line in enumerate(lines): + if open_re.match(line): + for j in range(i + 1, len(lines)): + if LUA_TABLE_CLOSE_RE.match(lines[j]): # `^\s*}` works for C++ too + return i, j + return i, len(lines) - 1 + return None + + +def parse_cpp_enum(lines, enum_name): + """Return (members, open_idx, close_idx, indent) for a C++ enum, ignoring + commented-out members.""" + body = find_enum_body(lines, enum_name) + if body is None: + return None + open_idx, close_idx = body + members = [] + indent = " " + for j in range(open_idx + 1, close_idx): + stripped = lines[j].lstrip() + if stripped.startswith("//") or not stripped: + continue + m = CPP_MEMBER_RE.match(lines[j]) + if m: + token = m.group(2) + value = int(token, 16) if token.lower().startswith("0x") else int(token) + members.append(CppMember(value, m.group(1), j)) + indent = lines[j][: len(lines[j]) - len(lines[j].lstrip())] + return members, open_idx, close_idx, indent + + +def find_address_spec(root): + """Auto-detect an `enum ... Address ... {` under src/, so addresses sync + automatically once such an enum is introduced. None today.""" + addr_re = re.compile(r"^\s*enum\b[^{;]*\bAddress\b[^{;]*\{") + for path in iter_src_files(root): + if not path.endswith((".h", ".hpp")): + continue + with open(path, "r") as f: + for line in f: + if addr_re.match(line): + return { + "name": "Address", + "path": os.path.relpath(path, root), + "lua_table": "known_addresses", + "style": "device", + "hexfmt": "0x%03X", + } + return None + + +def iter_src_files(root): + for dirpath, _dirs, files in os.walk(os.path.join(root, SRC_REL)): + for fn in sorted(files): + if fn.endswith(SRC_EXTS): + yield os.path.join(dirpath, fn) + +# ---- lint ------------------------------------------------------------------ + + +def lint_table(entries, path): + """Return a list of (lineno, name, reason) for dissector names that break a + hard rule.""" + problems = [] + seen = {} + for e in entries: + s = sanitize(e.name) + if not s or s[0].isdigit(): + problems.append((e.lineno, e.name, "starts with a digit")) + continue + if len(s) < 3: + problems.append((e.lineno, e.name, "shorter than 3 characters")) + key = s.upper() + if key in seen: + problems.append((e.lineno, e.name, + "collides with '%s' (line %d) after sanitize" + % (seen[key][0], seen[key][1]))) + else: + seen[key] = (e.name, e.lineno) + return problems + +# ---- reconciliation -------------------------------------------------------- + + +class Drift: + def __init__(self, spec): + self.spec = spec + self.renames = [] # (old_name, new_name, value) + self.adds = [] # (value, new_name) + self.cpp_only = [] # (value, name) + + def clean(self): + return not (self.renames or self.adds or self.cpp_only) + + +def reconcile(spec, lua_entries, members): + stylefn = STYLERS[spec["style"]] + drift = Drift(spec) + cpp_by_val = {m.value: m for m in members} + lua_vals = set() + for e in lua_entries: + lua_vals.add(e.value) + target = stylefn(e.name) + if e.value in cpp_by_val: + cur = cpp_by_val[e.value].name + if canonical(cur) != canonical(e.name): + drift.renames.append((cur, target, e.value)) + else: + drift.adds.append((e.value, target)) + for m in members: + if m.value not in lua_vals: + drift.cpp_only.append((m.value, m.name)) + return drift + +# ---- fix: rewrite enum bodies + references --------------------------------- + + +def compute_insertions(adds, lua_order, present_idx, close_idx, indent, hexfmt): + """Map each added value to the line index it should be inserted before, + mirroring the Lua table order. Returns {index: [rendered_line, ...]}.""" + groups = {} + order_pos = {v: i for i, v in enumerate(lua_order)} + for value, name in adds: # adds are already in Lua order + pos = order_pos[value] + ins = None + for j in range(pos - 1, -1, -1): + if lua_order[j] in present_idx: + ins = present_idx[lua_order[j]] + 1 + break + if ins is None: + for j in range(pos + 1, len(lua_order)): + if lua_order[j] in present_idx: + ins = present_idx[lua_order[j]] + break + if ins is None: + ins = close_idx + line = "%s%s = %s,\n" % (indent, name, hexfmt % value) + groups.setdefault(ins, []).append(line) + return groups + + +def apply_additions(path, spec, lua_entries, drift): + if not drift.adds: + return + lines = read_lines(path) + parsed = parse_cpp_enum(lines, spec["name"]) + members, _open_idx, close_idx, indent = parsed + present_idx = {m.value: m.idx for m in members} + lua_order = [e.value for e in lua_entries] + groups = compute_insertions(drift.adds, lua_order, present_idx, + close_idx, indent, spec["hexfmt"]) + for ins in sorted(groups, reverse=True): + lines[ins:ins] = groups[ins] + with open(path, "w") as f: + f.writelines(lines) + + +def replace_in_code(content, pattern, repl): + """Apply `pattern` only to code, leaving comments, string literals and char + literals untouched -- a bare token like a renamed enum member must not be + rewritten where it merely appears in prose (e.g. `CSMA/CD` in a comment).""" + out = [] + i = 0 + n = len(content) + while i < n: + c = content[i] + nxt = content[i + 1] if i + 1 < n else "" + if c == "/" and nxt == "/": + j = content.find("\n", i) + j = n if j == -1 else j + out.append(content[i:j]) + i = j + elif c == "/" and nxt == "*": + j = content.find("*/", i + 2) + j = n if j == -1 else j + 2 + out.append(content[i:j]) + i = j + elif c == '"' or (c == "'" and not (i > 0 and (content[i - 1].isalnum() + or content[i - 1] == "_"))): + # string, or a char literal (not a C++ digit separator like 1'000) + quote = c + j = i + 1 + while j < n: + if content[j] == "\\": + j += 2 + continue + if content[j] == quote: + j += 1 + break + j += 1 + out.append(content[i:j]) + i = j + else: + j = i + while j < n: + cj = content[j] + cj1 = content[j + 1] if j + 1 < n else "" + if cj == '"': + break + if cj == "'" and not (content[j - 1].isalnum() or content[j - 1] == "_"): + break + if cj == "/" and (cj1 == "/" or cj1 == "*"): + break + j += 1 + out.append(pattern.sub(repl, content[i:j])) + i = j + return "".join(out) + + +# A token right after one of these keywords is *introducing a name* (a type, +# enum, etc.), not referencing our enum member -- skip it so a member name that +# collides with an unrelated type (e.g. `enum CD { ... }`) is left alone. +DECL_KEYWORD_RE = re.compile(r"\b(?:enum|class|struct|union|namespace|typedef)\s+$") + + +def apply_renames(root, renames, verbose): + """Replace whole-word code occurrences of every renamed member across src/. + A single simultaneous pass (alternation) avoids A->B->C chaining.""" + if not renames: + return + mapping = {old: new for old, new, _v in renames} + pattern = re.compile(r"\b(%s)\b" % "|".join(re.escape(o) for o in mapping)) + for path in iter_src_files(root): + with open(path, "r") as f: + content = f.read() + count = [0] + + def repl(m): + if DECL_KEYWORD_RE.search(m.string[: m.start()]): + return m.group(0) # a declaration, not a member reference + count[0] += 1 + return mapping[m.group(1)] + + new_content = replace_in_code(content, pattern, repl) + if new_content != content: + with open(path, "w") as f: + f.write(new_content) + if verbose: + print(" %s: %d replacement(s)" % (os.path.relpath(path, root), count[0])) + +# ---- reporting ------------------------------------------------------------- + + +def print_lint(spec, path, problems): + for lineno, name, reason in problems: + print(" %s:%d: %r -- %s" % (path, lineno, name, reason)) + + +def print_drift(drift): + spec = drift.spec + for old, new, value in drift.renames: + print(" rename %s -> %s (0x%02X)" % (old, new, value)) + for value, name in drift.adds: + print(" add %s = 0x%02X" % (name, value)) + for value, name in drift.cpp_only: + print(" ERROR %s (0x%02X) is in C++ %s but not the dissector" + % (name, value, spec["name"])) + +# ---- driver ---------------------------------------------------------------- + + +def build_specs(root): + specs = list(ENUM_SPECS) + addr = find_address_spec(root) + if addr: + specs.append(addr) + return specs, addr is not None + + +def load(root, spec, lua_lines): + entries = parse_lua_table(lua_lines, spec["lua_table"]) + if entries is None: + sys.exit("error: Lua table '%s' not found" % spec["lua_table"]) + parsed = parse_cpp_enum(read_lines(os.path.join(root, spec["path"])), spec["name"]) + if parsed is None: + sys.exit("error: C++ enum '%s' not found in %s" % (spec["name"], spec["path"])) + return entries, parsed[0] + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("--fix", action="store_true", + help="apply the sync (rewrite enums + references)") + parser.add_argument("--lint", action="store_true", + help="validate dissector names only") + parser.add_argument("--verbose", action="store_true", + help="list every reference replacement made by --fix") + parser.add_argument("--root", + help="repo root (default: inferred from this script)") + args = parser.parse_args() + + root = args.root or os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + lua_path = os.path.join(root, LUA_REL) + lua_lines = read_lines(lua_path) + specs, have_address = build_specs(root) + if not have_address: + print("note: no Address enum found in src/ -- addresses not synced") + + # ---- lint (all modes) ---- + lint_failed = False + for spec in specs: + entries = parse_lua_table(lua_lines, spec["lua_table"]) + if entries is None: + sys.exit("error: Lua table '%s' not found" % spec["lua_table"]) + problems = lint_table(entries, LUA_REL) + if problems: + lint_failed = True + print("dissector name problems in %s (%s):" % (spec["lua_table"], LUA_REL)) + print_lint(spec, LUA_REL, problems) + + if args.lint: + if lint_failed: + print("lint: FAILED") + return 1 + print("lint: ok") + return 0 + + # ---- reconcile ---- + drifts = [] + for spec in specs: + entries, members = load(root, spec, lua_lines) + drifts.append((spec, entries, reconcile(spec, entries, members))) + + has_cpp_only = any(d.cpp_only for _s, _e, d in drifts) + has_drift = any(not d.clean() for _s, _e, d in drifts) + + if args.fix: + if lint_failed: + print("fix aborted: resolve the dissector name problems above first") + return 1 + if has_cpp_only: + for _s, _e, d in drifts: + if d.cpp_only: + print("%s:" % d.spec["name"]) + print_drift(d) + print("fix aborted: C++ has values the dissector lacks (see ERRORs)") + return 1 + all_renames = [] + for spec, entries, drift in drifts: + apply_additions(os.path.join(root, spec["path"]), spec, entries, drift) + all_renames.extend(drift.renames) + apply_renames(root, all_renames, args.verbose) + for spec, entries, drift in drifts: + if not drift.clean(): + print("%s: %d rename(s), %d addition(s)" + % (spec["name"], len(drift.renames), len(drift.adds))) + print("fix: applied" if has_drift else "fix: already in sync") + return 0 + + # ---- default: report ---- + for spec, _entries, drift in drifts: + if not drift.clean(): + print("%s drift:" % spec["name"]) + print_drift(drift) + if lint_failed or has_drift: + print("out of sync -- run: scripts/sync_avclan_enums.py --fix") + return 1 + print("enums in sync with the dissector") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/avclan/avclan.h b/src/avclan/avclan.h index 51d9271..eabd534 100644 --- a/src/avclan/avclan.h +++ b/src/avclan/avclan.h @@ -31,7 +31,8 @@ enum AVCLAN_ENUM_CLASS Action : uint8_t { // LAN related List_Functions_Req = 0x00, List_Functions_Resp = 0x10, - Restart_Lan = 0x01, + Lan_Init = 0x01, + Lan_Init_Complete = 0x58, // Lan_Startup_Complete = 0x58, Lancheck_End_Req = 0x08, Lancheck_End_Resp = 0x18, @@ -50,7 +51,7 @@ enum AVCLAN_ENUM_CLASS Action : uint8_t { Disable_Function_Req = 0x43, Disable_Function_Resp = 0x53, - Current_Function = 0x45, + Advertise_Function = 0x45, General_Query = 0x46, // Events @@ -60,6 +61,7 @@ enum AVCLAN_ENUM_CLASS Action : uint8_t { // Physical interface Backlight_Adjust = 0x59, Beep = 0x60, + Screen_Press = 0x78, Eject = 0x80, Disc_Up = 0x90, Disc_Down = 0x91, @@ -69,6 +71,7 @@ enum AVCLAN_ENUM_CLASS Action : uint8_t { Track_Rewind = 0x99, Pwrvol_Knob_Righthand_Turn = 0x9c, Pwrvol_Knob_Lefthand_Turn = 0x9d, + Tape_Not_Ready = 0x9f, CD_Enable_Repeat = 0xa0, CD_Disable_Repeat = 0xa1, CD_Enable_Disk_Repeat = 0xa3, @@ -83,21 +86,21 @@ enum AVCLAN_ENUM_CLASS Action : uint8_t { CD_Disable_Disk_Random = 0xb4, // Requests and Response pairs - Initial_Report_Request = 0xe0, - Initial_Report_Response = 0xf0, + Initial_Report_Req = 0xe0, + Initial_Report_Resp = 0xf0, - Playback_Request = 0xe2, - Playback_Report = 0xf2, + Playback_Req = 0xe2, + Playback_Resp = 0xf2, - Loading_Request2 = 0xe4, - Loading_Response2 = 0xf4, + Loading_Req = 0xe4, + Loading_Resp = 0xf4, - Request_Track_Name = 0xed, - Report_Track_Name = 0xfd, + Track_Name_Req = 0xed, + Track_Name_Resp = 0xfd, // Reports - Status_Report = 0xf1, // Typically unprompted, sent to Device::STATUS - Loading_Status_Report = 0xf3, // Typically unprompted, sent to Device::STATUS + Playback_Status = 0xf1, // Typically unprompted, sent to Device::STATUS + Loading_Status = 0xf3, // Typically unprompted, sent to Device::STATUS Report_TOC = 0xf9, }; diff --git a/src/avclan/cdchanger.cc b/src/avclan/cdchanger.cc index 79b0a28..842d6d4 100644 --- a/src/avclan/cdchanger.cc +++ b/src/avclan/cdchanger.cc @@ -19,7 +19,7 @@ using namespace avclan; constexpr uint8_t cdloading_resp[] = { to_underlying(Device::CD_CHANGER), to_underlying(Device::STATUS), - to_underlying(Action::Loading_Status_Report), + to_underlying(Action::Loading_Status), 0x00, 0x01, 0x00, @@ -116,14 +116,14 @@ void CDChanger::handle(const Frame *in, Frame *out) { } break; } - case Initial_Report_Request: { + case Initial_Report_Req: { out->is_unicast = true; // No knowledge/understanding of field meaning/interpretation const uint8_t cdinitreport_resp[] = { 0x00, to_underlying(Device::CD_CHANGER), to_underlying(from), - to_underlying(Initial_Report_Response), + to_underlying(Initial_Report_Resp), 0x01, 0x31, 0x10, @@ -134,22 +134,22 @@ void CDChanger::handle(const Frame *in, Frame *out) { out->reaction = r_SendOnly; break; } - case Playback_Request: + case Playback_Req: out->data[0] = 0x00; out->data[1] = to_underlying(Device::CD_CHANGER); out->data[2] = to_underlying(from); - out->data[3] = to_underlying(Playback_Report); + out->data[3] = to_underlying(Playback_Resp); out->length = WIRE_SIZE + 4; serialize(&out->data[4]); out->is_unicast = true; out->reaction = r_SendOnly; break; - case Loading_Request2: + case Loading_Req: out->data[0] = 0x00; out->length = sizeof(cdloading_resp) + 1; memcpy(&out->data[1], cdloading_resp, sizeof(cdloading_resp)); out->data[2] = to_underlying(from); - out->data[3] = to_underlying(Loading_Response2); + out->data[3] = to_underlying(Loading_Resp); out->is_unicast = true; out->reaction = r_SendOnly; break; @@ -276,7 +276,7 @@ void CDChanger::react(Frame *out, detail::Error::Send err) { case r_Ejection: { const uint8_t play[] = {0x00, to_underlying(Device::COMM_CTRL), - to_underlying(Device::COMM_v1), + to_underlying(Device::COMMUNICATION_V1), to_underlying(Action::Insertion), to_underlying(Device::CD_CHANGER), 0x01}; @@ -291,7 +291,7 @@ void CDChanger::react(Frame *out, detail::Error::Send err) { out->length = sizeof(cdloading_resp) + 1; memcpy(out->data, cdloading_resp, sizeof(cdloading_resp)); out->data[1] = to_underlying(Device::STATUS); - out->data[2] = to_underlying(Action::Loading_Status_Report); + out->data[2] = to_underlying(Action::Loading_Status); out->reaction = r_SendOnly; break; case r_TrackChange: @@ -411,7 +411,7 @@ void CDChanger::generateStatus(Frame *status, bool is_unicast, *data++ = 0x00; *data++ = to_underlying(Device::CD_CHANGER); *data++ = to_underlying(to); - *data++ = to_underlying(Action::Status_Report); + *data++ = to_underlying(Action::Playback_Status); serialize(data); } diff --git a/src/avclan/device.hpp b/src/avclan/device.hpp index 1351f87..c57cb75 100644 --- a/src/avclan/device.hpp +++ b/src/avclan/device.hpp @@ -14,20 +14,35 @@ namespace avclan { enum class Device : uint8_t { LAN = 0x00, COMM_CTRL = 0x01, - COMM_v1 = 0x11, - COMM_v2 = 0x12, - SW = 0x21, + COMMUNICATION_V1 = 0x11, + COMMUNICATION_V2 = 0x12, + SW_AUDIO = 0x21, SW_NAME = 0x23, SW_CONVERTING = 0x24, CMD_SW = 0x25, STATUS = 0x31, + INFO_DISPLAY2 = 0x32, BEEP_HU = 0x28, BEEP_SPEAKERS = 0x29, + FRONT_PSNG_MONITOR = 0x34, + CD_CHANGER2 = 0x43, + BLUETOOTH_TEL = 0x55, + INFO_DRAWING = 0x56, + NAV_ECU = 0x58, + CAMERA = 0x5C, + CLIMATE_DRAWING = 0x5D, + AUDIO_DRAWING = 0x5E, + TRIP_INFO_DRAWING = 0x5F, TUNER = 0x60, TAPE_DECK = 0x61, - CD = 0x62, + CD_SINGLE = 0x62, CD_CHANGER = 0x63, AUDIO_AMP = 0x74, + GPS = 0x80, + VOICE_CTRL = 0x85, + XM_TUNER = 0xC0, + CLIMATE_CTRL_DEV = 0xE0, + TRIP_INFO = 0xE5, }; template diff --git a/src/avclan/peripheral.hpp b/src/avclan/peripheral.hpp index 6243532..be61a75 100644 --- a/src/avclan/peripheral.hpp +++ b/src/avclan/peripheral.hpp @@ -87,19 +87,19 @@ public: out->data[3] = to_underlying(Lancheck_End_Resp); out->reaction = 1; break; - case PACK3(COMM_v1, COMM_CTRL, to_underlying(Current_Function)): - case PACK3(COMM_v2, COMM_CTRL, to_underlying(Current_Function)): + case PACK3(COMMUNICATION_V1, COMM_CTRL, to_underlying(Advertise_Function)): + case PACK3(COMMUNICATION_V2, COMM_CTRL, to_underlying(Advertise_Function)): ((Devs::id == static_cast(b3) ? std::get(devices_).enable(out), 0 : 0), ...); break; - case PACK3(COMM_v1, COMM_CTRL, to_underlying(Ping_Req)): - case PACK3(COMM_v2, COMM_CTRL, to_underlying(Ping_Req)): { + case PACK3(COMMUNICATION_V1, COMM_CTRL, to_underlying(Ping_Req)): + case PACK3(COMMUNICATION_V2, COMM_CTRL, to_underlying(Ping_Req)): { out->is_unicast = true; const uint8_t ping_resp[] = {0x00, to_underlying(COMM_CTRL), - to_underlying(COMM_v1), + to_underlying(COMMUNICATION_V1), to_underlying(Ping_Resp), 0xFF, b3}; @@ -108,13 +108,13 @@ public: out->reaction = 1; break; } - case PACK3(COMM_v1, COMM_CTRL, to_underlying(List_Functions_Req)): - case PACK3(COMM_v2, COMM_CTRL, to_underlying(List_Functions_Req)): { + case PACK3(COMMUNICATION_V1, COMM_CTRL, to_underlying(List_Functions_Req)): + case PACK3(COMMUNICATION_V2, COMM_CTRL, to_underlying(List_Functions_Req)): { controller_ = in->controller_addr; out->peripheral_addr = controller_; out->is_unicast = true; const uint8_t list_functions_resp[] = { - 0x00, to_underlying(COMM_CTRL), to_underlying(COMM_v1), + 0x00, to_underlying(COMM_CTRL), to_underlying(COMMUNICATION_V1), to_underlying(List_Functions_Resp), to_underlying(CD_CHANGER)}; out->length = sizeof(list_functions_resp); memcpy(out->data, list_functions_resp, sizeof(list_functions_resp)); diff --git a/src/sniffer.cc b/src/sniffer.cc index 1724074..ad374f1 100644 --- a/src/sniffer.cc +++ b/src/sniffer.cc @@ -158,7 +158,7 @@ int main() { { const uint8_t play[] = {0x00, to_underlying(COMM_CTRL), - to_underlying(COMM_v1), + to_underlying(COMMUNICATION_V1), to_underlying(Ejection), to_underlying(CD_CHANGER), 0x01};