建立 MAG160C 逆向工程交接仓库

This commit is contained in:
ZXCLI
2026-08-11 19:08:44 +08:00
commit 8409b27ba3
3135 changed files with 534408 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
# Subagent-Driven Development Progress
- Task 1: complete (review clean of Critical/Important issues; direct g++ verification passed; CMake unavailable in environment)
- Task 2: complete (review clean after oversized-payload fix; direct g++ verification passed; CMake unavailable in environment)
- Task 3: complete (review clean of Critical/Important issues; direct g++ verification passed; CMake unavailable in environment; minor coverage/internal validation notes recorded)
+127
View File
@@ -0,0 +1,127 @@
# Task 1 Brief: Build scaffold and public C ABI lifecycle
## Global constraints
- Use only original source code; do not link proprietary SDK binaries.
- Current workspace is not a git repository; replace commit steps with verification plus updates to progress.md.
- Keep public C ABI free of C++ types.
- Use C++17.
- libusb-1.0 is the only native runtime dependency for the core library.
- Firmware update commands are not exposed as an easy accidental CLI action.
- IR protocol gaps return MAG160C_ERR_PROTOCOL_UNKNOWN or MAG160C_ERR_UNSUPPORTED.
## Files
- Create: CMakeLists.txt
- Create: include/mag160c/mag160c.h
- Create: src/core/error.hpp
- Create: src/core/error.cpp
- Create: src/core/context.hpp
- Create: src/core/context.cpp
- Create: src/c_api.cpp
- Create: tests/cpp/test_c_api.cpp
- Update: progress.md with the checkpoint at the end.
## Interfaces to produce
- mag160c_error_t
- opaque mag160c_context_t
- opaque mag160c_ir_device_t
- opaque mag160c_tcm_device_t
- mag160c_error_t mag160c_init(mag160c_context_t** out_ctx)
- void mag160c_shutdown(mag160c_context_t* ctx)
- const char* mag160c_last_error(void)
- const char* mag160c_error_name(mag160c_error_t code)
## Required public error enum
```c
typedef enum mag160c_error_t {
MAG160C_OK = 0,
MAG160C_ERR_INVALID_ARGUMENT = 1,
MAG160C_ERR_NO_DEVICE = 2,
MAG160C_ERR_PERMISSION = 3,
MAG160C_ERR_USB = 4,
MAG160C_ERR_TIMEOUT = 5,
MAG160C_ERR_CHECKSUM = 6,
MAG160C_ERR_PROTOCOL_UNKNOWN = 7,
MAG160C_ERR_UNSUPPORTED = 8,
MAG160C_ERR_INTERNAL = 9
} mag160c_error_t;
```
## Required test assertions
Create tests/cpp/test_c_api.cpp and include assertions equivalent to:
```cpp
assert(std::strcmp(mag160c_error_name(MAG160C_OK), "MAG160C_OK") == 0);
assert(std::strcmp(mag160c_error_name(MAG160C_ERR_PROTOCOL_UNKNOWN), "MAG160C_ERR_PROTOCOL_UNKNOWN") == 0);
assert(std::strcmp(mag160c_error_name(static_cast<mag160c_error_t>(9999)), "MAG160C_ERR_UNKNOWN_CODE") == 0);
assert(mag160c_init(nullptr) == MAG160C_ERR_INVALID_ARGUMENT);
mag160c_context_t* ctx = nullptr;
assert(mag160c_init(&ctx) == MAG160C_OK);
assert(ctx != nullptr);
mag160c_shutdown(ctx);
```
## Required CMake behavior
- Minimum CMake 3.16.
- Project name mag160c, C and CXX languages.
- Options:
- MAG160C_BUILD_TESTS ON by default.
- MAG160C_BUILD_CLI ON by default.
- C++ standard 17, required, no compiler extensions.
- Use PkgConfig to find libusb-1.0 if available.
- Build shared library target mag160c_core from:
- src/core/error.cpp
- src/core/context.cpp
- src/c_api.cpp
- Public include directory: include.
- Private include directory: src.
- If libusb is found, define MAG160C_HAS_LIBUSB=1 and link include/library variables.
- If libusb is absent, define MAG160C_HAS_LIBUSB=0 and still build.
- When tests are enabled, build test_c_api and register it with CTest.
## Required implementation behavior
- last error is thread-local.
- mag160c_error_name returns stable string names for every enum value and MAG160C_ERR_UNKNOWN_CODE for unknown values.
- Context construction succeeds without hardware.
- When libusb is absent at build time, Context records has_libusb as false but still constructs.
- mag160c_init rejects a null output pointer, sets last_error mentioning out_ctx, and returns MAG160C_ERR_INVALID_ARGUMENT.
- mag160c_init allocates a context on success, clears last_error, and returns MAG160C_OK.
- mag160c_shutdown accepts null safely through delete behavior.
- mag160c_last_error returns the thread-local message.
## Verification
Run:
```powershell
cmake -S . -B build -DMAG160C_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build --output-on-failure
```
Expected: test_c_api passes.
## Progress checkpoint
Append this line to progress.md after verification:
```markdown
- Implemented Task 1 scaffold: CMake, public C ABI lifecycle, error names, and lifecycle tests.
```
## Report contract
Write a report to .superpowers/sdd/task-1-report.md with:
- status: DONE, DONE_WITH_CONCERNS, NEEDS_CONTEXT, or BLOCKED
- files created/modified
- exact commands run
- test results
- concerns, if any
- self-review notes
+48
View File
@@ -0,0 +1,48 @@
# Task 1 Report
status: DONE_WITH_CONCERNS
## Files created/modified
- CMakeLists.txt
- include/mag160c/mag160c.h
- src/core/error.hpp
- src/core/error.cpp
- src/core/context.hpp
- src/core/context.cpp
- src/c_api.cpp
- tests/cpp/test_c_api.cpp
- progress.md
## Commands run
```powershell
cmake -S . -B build -DMAG160C_BUILD_TESTS=ON
```
Result: failed because cmake is not installed or not on PATH.
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task1_test.exe
.\build_task1_test.exe
```
Result: passed with exit code 0 and no assertion failures.
## Test results
- CMake/CTest verification could not run because `cmake` is unavailable in the environment.
- Direct MinGW C++17 compile/run verification passed.
## Concerns
- The planned CMake verification remains unverified until CMake is installed or available on PATH.
- A generated local executable `build_task1_test.exe` exists from substitute verification.
## Self-review notes
- Public C ABI is free of C++ types.
- Unknown error codes return MAG160C_ERR_UNKNOWN_CODE.
- Null init output pointer returns MAG160C_ERR_INVALID_ARGUMENT and last_error mentions out_ctx.
- Successful init clears last_error and returns a non-null context.
- Context construction does not require hardware or libusb.
+173
View File
@@ -0,0 +1,173 @@
# Task 2 Brief: TCM frame codec
## Context
Task 1 is complete. Existing files include CMakeLists.txt, include/mag160c/mag160c.h, src/core/error.*, src/core/context.*, src/c_api.cpp, and tests/cpp/test_c_api.cpp.
CMake is currently unavailable on PATH in this environment. Still keep CMakeLists.txt correct, but if CMake verification cannot run, use direct MinGW g++ verification and record the substitution in the report.
## Global constraints
- Use only original source code; do not link proprietary SDK binaries.
- Current workspace is not a git repository; replace commit steps with verification plus updates to progress.md.
- Keep public C ABI free of C++ types.
- Use C++17.
- libusb-1.0 is the only native runtime dependency for the core library.
- Firmware update commands are not exposed as an easy accidental CLI action.
- IR protocol gaps return MAG160C_ERR_PROTOCOL_UNKNOWN or MAG160C_ERR_UNSUPPORTED.
## Files
- Create: src/core/tcm_frame.hpp
- Create: src/core/tcm_frame.cpp
- Create: tests/cpp/test_tcm_frame.cpp
- Modify: CMakeLists.txt
- Modify: include/mag160c/mag160c.h
- Modify: src/c_api.cpp
- Update: progress.md with the checkpoint at the end.
## Interfaces to produce
- mag160c::core::TcmFrame
- mag160c::core::checksum(const uint8_t* data, size_t begin, size_t end)
- mag160c::core::encode_tcm_frame(uint8_t main_cmd, uint8_t sub_cmd, uint16_t frame_id, const std::vector<uint8_t>& payload)
- mag160c::core::decode_tcm_frame(const uint8_t* data, size_t size, TcmFrame* out)
- mag160c_tcm_encode_frame C ABI
- mag160c_tcm_decode_header C ABI
## TCM frame rules
- Header byte 0 is 0x7e.
- Bytes 1..2 are big-endian body length.
- Byte 3 is additive checksum over bytes 0..2.
- Body is main, sub, frame high, frame low, payload, body checksum.
- Body checksum is additive checksum over body bytes before the final checksum byte.
- Body length is payload length + 5.
- Minimum valid full packet length is 9 bytes.
## Required exact test vector
Encoding main 0x02, sub 0x77, frame id 0x0001, payload {0x00, 0x05} must produce:
```text
7e 00 07 85 02 77 00 01 00 05 7f
```
## Required tests
Create tests/cpp/test_tcm_frame.cpp with:
- test_rotate_frame_encoding: exact bytes above.
- test_decode_rejects_bad_header_checksum: corrupt byte 3 and expect MAG160C_ERR_CHECKSUM.
- test_decode_roundtrip: encode then decode and assert main, sub, frame id, payload.
## Required C++ header
src/core/tcm_frame.hpp must declare:
```cpp
#pragma once
#include "mag160c/mag160c.h"
#include <cstddef>
#include <cstdint>
#include <vector>
namespace mag160c::core {
struct TcmFrame {
uint8_t main_cmd = 0;
uint8_t sub_cmd = 0;
uint16_t frame_id = 0;
std::vector<uint8_t> payload;
};
uint8_t checksum(const uint8_t* data, size_t begin, size_t end);
std::vector<uint8_t> encode_tcm_frame(uint8_t main_cmd, uint8_t sub_cmd, uint16_t frame_id, const std::vector<uint8_t>& payload);
mag160c_error_t decode_tcm_frame(const uint8_t* data, size_t size, TcmFrame* out);
}
```
## Required C ABI declarations
Add to include/mag160c/mag160c.h inside the extern C block:
```c
MAG160C_API mag160c_error_t mag160c_tcm_encode_frame(
uint8_t main_cmd,
uint8_t sub_cmd,
uint16_t frame_id,
const uint8_t* payload,
size_t payload_size,
uint8_t* out_bytes,
size_t out_capacity,
size_t* out_size
);
MAG160C_API mag160c_error_t mag160c_tcm_decode_header(
const uint8_t* data,
size_t size,
uint8_t* out_main_cmd,
uint8_t* out_sub_cmd,
uint16_t* out_frame_id,
size_t* out_payload_size
);
```
## Required C ABI behavior
- mag160c_tcm_encode_frame requires out_size.
- If output buffer is null or too small, set out_size to the required encoded size and return MAG160C_ERR_INVALID_ARGUMENT.
- If payload_size is nonzero and payload is null, return MAG160C_ERR_INVALID_ARGUMENT.
- On success, copy encoded bytes, set out_size, clear last error, return MAG160C_OK.
- mag160c_tcm_decode_header requires all output pointers.
- It should call decode_tcm_frame and return any error from it.
- On success, fill main, sub, frame id, and payload size.
## CMake update
- Add src/core/tcm_frame.cpp to mag160c_core sources.
- Add test_tcm_frame executable linked to mag160c_core.
- Give test_tcm_frame access to src private headers.
- Register test_tcm_frame with CTest.
## Verification
First try:
```powershell
cmake --build build
ctest --test-dir build --output-on-failure
```
If CMake is unavailable, run direct verification equivalent to:
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task2_c_api_test.exe
.\build_task2_c_api_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\c_api.cpp tests\cpp\test_tcm_frame.cpp -o build_task2_tcm_frame_test.exe
.\build_task2_tcm_frame_test.exe
```
Expected: both executables exit 0.
## Progress checkpoint
Append this line to progress.md after verification:
```markdown
- Implemented Task 2 TCM frame codec with byte-exact encode/decode tests and C ABI utility functions.
```
## Report contract
Write a report to .superpowers/sdd/task-2-report.md with:
- status: DONE, DONE_WITH_CONCERNS, NEEDS_CONTEXT, or BLOCKED
- files created/modified
- exact commands run
- test results
- concerns, if any
- self-review notes
+52
View File
@@ -0,0 +1,52 @@
# Task 2 Fix Brief: oversized TCM payload safety
## Finding to fix
Task 2 reviewer found an Important issue:
- In src/core/tcm_frame.cpp, payload.size() + 5U is cast to uint16_t. For payloads larger than 65530 bytes, the body length truncates, the vector is allocated too small, and the copy loop writes past the encoded vector.
- In src/c_api.cpp, mag160c_tcm_encode_frame encodes before checking output buffer, so even size-query calls with out_bytes == nullptr can trigger the overflow.
## Required fix
- Prevent oversized payloads from corrupting memory.
- Maximum payload size is 65530 bytes because body length is uint16_t and body length is payload_size + 5.
- C++ encode_tcm_frame must reject oversized payloads before allocation/copy.
- C ABI mag160c_tcm_encode_frame must return MAG160C_ERR_INVALID_ARGUMENT for oversized payloads and set last_error explaining the payload is too large.
- Add tests covering oversized payload handling.
## Suggested implementation
- In src/core/tcm_frame.cpp, add a helper constant or check:
- constexpr size_t MAX_TCM_PAYLOAD_SIZE = 0xffffU - 5U;
- if payload.size() > MAX_TCM_PAYLOAD_SIZE, throw std::length_error("TCM payload is too large");
- In src/c_api.cpp, validate payload_size before constructing payload_vec or calling encode_tcm_frame.
- Optionally catch std::length_error around encode_tcm_frame and map to MAG160C_ERR_INVALID_ARGUMENT for defense-in-depth.
## Required tests
Add at least one no-crash test:
- In tests/cpp/test_c_api.cpp, allocate a payload of size 65531 and call mag160c_tcm_encode_frame with out_bytes nullptr and capacity 0.
- Assert it returns MAG160C_ERR_INVALID_ARGUMENT.
- Assert out_size is 0.
- Assert mag160c_last_error() contains "too large".
Also add a core test if convenient:
- In tests/cpp/test_tcm_frame.cpp, call encode_tcm_frame with payload size 65531 and assert it throws std::length_error.
## Verification
Run direct verification:
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task2_c_api_test.exe
.\build_task2_c_api_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\c_api.cpp tests\cpp\test_tcm_frame.cpp -o build_task2_tcm_frame_test.exe
.\build_task2_tcm_frame_test.exe
```
## Report contract
Append a fix report section to .superpowers/sdd/task-2-report.md and return status plus one-line test summary.
+72
View File
@@ -0,0 +1,72 @@
# Task 2 Report
status: DONE_WITH_CONCERNS
## Files created/modified
- CMakeLists.txt
- include/mag160c/mag160c.h
- src/core/tcm_frame.hpp
- src/core/tcm_frame.cpp
- src/c_api.cpp
- tests/cpp/test_c_api.cpp
- tests/cpp/test_tcm_frame.cpp
- progress.md
## Commands run
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task2_c_api_test.exe
.\build_task2_c_api_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\c_api.cpp tests\cpp\test_tcm_frame.cpp -o build_task2_tcm_frame_test.exe
.\build_task2_tcm_frame_test.exe
```
## Test results
- Direct MinGW C++17 compile/run verification passed for test_c_api.
- Direct MinGW C++17 compile/run verification passed for test_tcm_frame.
## Concerns
- CMake/CTest verification remains unavailable because cmake is not installed or not on PATH in this environment.
- Generated local executables build_task2_c_api_test.exe and build_task2_tcm_frame_test.exe exist from substitute verification.
## Self-review notes
- TCM encode exact vector for rotate +5 is implemented and tested.
- Header checksum and body checksum use additive uint8_t wrapping.
- C ABI encode reports required size when output buffer is too small.
- C ABI decode_header delegates to decode_tcm_frame and returns parsed main/sub/frame/payload size.
- Public header now includes stddef.h and stdint.h because Task 2 introduced size_t and fixed-width integer types.
## Fix report: oversized TCM payload safety
status: DONE
### Files modified
- src/core/tcm_frame.cpp
- src/c_api.cpp
- tests/cpp/test_c_api.cpp
- tests/cpp/test_tcm_frame.cpp
### Commands run
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task2_c_api_test.exe
.\build_task2_c_api_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\c_api.cpp tests\cpp\test_tcm_frame.cpp -o build_task2_tcm_frame_test.exe
.\build_task2_tcm_frame_test.exe
```
### Test results
- Direct MinGW C++17 compile/run verification passed for test_c_api.
- Direct MinGW C++17 compile/run verification passed for test_tcm_frame.
### Self-review
- Oversized payloads are rejected before allocation/copy.
- C ABI oversized payload path returns MAG160C_ERR_INVALID_ARGUMENT, sets out_size to 0, and sets last_error containing "too large".
- Core encode_tcm_frame oversized payload path is covered by a std::length_error test.
+180
View File
@@ -0,0 +1,180 @@
# Task 3 Brief: TCM command builders
## Context
Tasks 1 and 2 are complete. Existing TCM frame codec is in src/core/tcm_frame.* and C ABI frame utilities are in src/c_api.cpp.
CMake is unavailable on PATH in this environment. Keep CMakeLists.txt correct, but use direct MinGW g++ verification if CMake cannot run.
## Global constraints
- Use only original source code; do not link proprietary SDK binaries.
- Current workspace is not a git repository; replace commit steps with verification plus updates to progress.md.
- Keep public C ABI free of C++ types.
- Use C++17.
- libusb-1.0 is the only native runtime dependency for the core library.
- Firmware update commands are not exposed as an easy accidental CLI action.
- IR protocol gaps return MAG160C_ERR_PROTOCOL_UNKNOWN or MAG160C_ERR_UNSUPPORTED.
## Files
- Create: src/core/transport.hpp
- Create: src/core/tcm_device.hpp
- Create: src/core/tcm_device.cpp
- Create: tests/cpp/test_tcm_device.cpp
- Modify: CMakeLists.txt
- Modify: include/mag160c/mag160c.h
- Modify: src/c_api.cpp
- Update: progress.md with the checkpoint.
## Interfaces to produce
- mag160c::core::Transport
- mag160c::core::TcmCommandBuilder
- mag160c_tcm_build_rotate_frame
- mag160c_tcm_build_light_frame
## Required transport interface
```cpp
class Transport {
public:
virtual ~Transport() = default;
virtual mag160c_error_t write(const std::vector<uint8_t>& bytes) = 0;
virtual mag160c_error_t read(std::vector<uint8_t>* out, int timeout_ms) = 0;
};
```
## Required TCM command behavior
- Frame id starts at 1.
- Frame id increments after each encoded frame.
- Frame id wraps to 1 before reaching 0x8000.
- rotate clamps input to [-128, 128].
- rotate payload is [direction, magnitude], direction 0 for positive and 1 for negative.
- light payload is [on, red, green, blue].
- red/green/blue/yellow channel value is 0xff.
- off payload is [0, 0, 0, 0].
- light steady sub is 0x31, blink is 0x32, breath is 0x33.
- all observed TCM main commands here use main 0x02.
## Required exact test vectors
- rotate +5 with initial frame id 1:
```text
7e 00 07 85 02 77 00 01 00 05 7f
```
- after one prior command, rotate -3 with frame id 2:
```text
7e 00 07 85 02 77 00 02 01 03 7f
```
- green blink with initial frame id 1:
```text
7e 00 09 87 02 32 00 01 01 00 ff 00 35
```
## Required C ABI declarations
Add to include/mag160c/mag160c.h:
```c
typedef enum mag160c_tcm_light_color_t {
MAG160C_TCM_LIGHT_OFF = 0,
MAG160C_TCM_LIGHT_RED = 1,
MAG160C_TCM_LIGHT_GREEN = 2,
MAG160C_TCM_LIGHT_BLUE = 3,
MAG160C_TCM_LIGHT_YELLOW = 4
} mag160c_tcm_light_color_t;
typedef enum mag160c_tcm_light_mode_t {
MAG160C_TCM_LIGHT_STEADY = 0,
MAG160C_TCM_LIGHT_BLINK = 1,
MAG160C_TCM_LIGHT_BREATH = 2
} mag160c_tcm_light_mode_t;
MAG160C_API mag160c_error_t mag160c_tcm_build_rotate_frame(
int angle,
uint8_t* out_bytes,
size_t out_capacity,
size_t* out_size
);
MAG160C_API mag160c_error_t mag160c_tcm_build_light_frame(
mag160c_tcm_light_color_t color,
mag160c_tcm_light_mode_t mode,
uint8_t* out_bytes,
size_t out_capacity,
size_t* out_size
);
```
## Required C ABI behavior
- Build helpers are dry-run byte builders only; they do not talk to USB.
- If out_size is null, return MAG160C_ERR_INVALID_ARGUMENT.
- If output buffer is null or too small, set out_size to the required frame size and return MAG160C_ERR_INVALID_ARGUMENT.
- On success, copy bytes, set out_size, clear last_error, return MAG160C_OK.
- Invalid light color or mode returns MAG160C_ERR_INVALID_ARGUMENT and sets a useful last_error.
## Required tests
Create tests/cpp/test_tcm_device.cpp with:
- test_rotate_positive_payload exact vector.
- test_rotate_negative_payload_and_frame_increment exact vector.
- test_green_blink_payload exact vector.
Also extend tests/cpp/test_c_api.cpp if convenient to check the new dry-run C ABI for rotate +5.
## CMake update
- Add src/core/tcm_device.cpp to mag160c_core sources.
- Add test_tcm_device executable linked to mag160c_core.
- Give test_tcm_device access to src private headers.
- Register test_tcm_device with CTest.
## Verification
First try:
```powershell
cmake --build build
ctest --test-dir build --output-on-failure
```
If CMake is unavailable, run:
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task3_c_api_test.exe
.\build_task3_c_api_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\c_api.cpp tests\cpp\test_tcm_frame.cpp -o build_task3_tcm_frame_test.exe
.\build_task3_tcm_frame_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\c_api.cpp tests\cpp\test_tcm_device.cpp -o build_task3_tcm_device_test.exe
.\build_task3_tcm_device_test.exe
```
Expected: all executables exit 0.
## Progress checkpoint
Append this line to progress.md after verification:
```markdown
- Implemented Task 3 TCM command builder, dry-run C ABI helpers, and command payload tests.
```
## Report contract
Write a report to .superpowers/sdd/task-3-report.md with:
- status: DONE, DONE_WITH_CONCERNS, NEEDS_CONTEXT, or BLOCKED
- files created/modified
- exact commands run
- test results
- concerns, if any
- self-review notes
+44
View File
@@ -0,0 +1,44 @@
# Task 3 Report
status: DONE_WITH_CONCERNS
## Files created/modified
- CMakeLists.txt
- include/mag160c/mag160c.h
- src/core/transport.hpp
- src/core/tcm_device.hpp
- src/core/tcm_device.cpp
- src/c_api.cpp
- tests/cpp/test_c_api.cpp
- tests/cpp/test_tcm_device.cpp
- progress.md
## Commands run
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task3_c_api_test.exe
.\build_task3_c_api_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\c_api.cpp tests\cpp\test_tcm_frame.cpp -o build_task3_tcm_frame_test.exe
.\build_task3_tcm_frame_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\c_api.cpp tests\cpp\test_tcm_device.cpp -o build_task3_tcm_device_test.exe
.\build_task3_tcm_device_test.exe
```
## Test results
- Direct MinGW C++17 compile/run verification passed for test_c_api.
- Direct MinGW C++17 compile/run verification passed for test_tcm_frame.
- Direct MinGW C++17 compile/run verification passed for test_tcm_device.
## Concerns
- CMake/CTest verification remains unavailable because cmake is not installed or not on PATH.
- Generated local executables build_task3_c_api_test.exe, build_task3_tcm_frame_test.exe, and build_task3_tcm_device_test.exe exist from substitute verification.
## Self-review notes
- TcmCommandBuilder starts frame ids at 1 and increments them.
- Rotate frames clamp to [-128, 128] and encode [direction, magnitude].
- Light frames encode [on, red, green, blue] with 0xff channel values.
- C ABI dry-run builders report required size on too-small buffers and validate invalid light color/mode.
+176
View File
@@ -0,0 +1,176 @@
# Task 4 Brief: USB discovery and endpoint model
## Context
Tasks 1-3 are complete. CMake remains unavailable on PATH; keep CMakeLists.txt correct but use direct MinGW g++ verification if CMake cannot run.
## Global constraints
- Use only original source code; do not link proprietary SDK binaries.
- Current workspace is not a git repository; replace commit steps with verification plus updates to progress.md.
- Keep public C ABI free of C++ types.
- Use C++17.
- libusb-1.0 is the only native runtime dependency for the core library.
- Firmware update commands are not exposed as an easy accidental CLI action.
- IR protocol gaps return MAG160C_ERR_PROTOCOL_UNKNOWN or MAG160C_ERR_UNSUPPORTED.
## Files
- Create: src/core/device.hpp
- Create: src/core/device.cpp
- Create: tests/cpp/test_device_model.cpp
- Modify: CMakeLists.txt
- Modify: include/mag160c/mag160c.h
- Modify: src/c_api.cpp
- Update: progress.md with the checkpoint.
## Interfaces to produce
- mag160c::core::EndpointType
- mag160c::core::EndpointDescriptor
- mag160c::core::EndpointPair
- mag160c::core::DeviceInfo
- mag160c::core::is_in_endpoint
- mag160c::core::find_bulk_pair
- mag160c::core::list_devices
- mag160c_list_devices C ABI
- mag160c_free_device_list C ABI
## Required constants and structs
```cpp
constexpr uint16_t MAG_IR_VENDOR_ID = 0x833c;
constexpr uint16_t MAG_IR_PRODUCT_ID = 0x0001;
enum class EndpointType {
Other,
Bulk,
Interrupt,
Isochronous
};
struct EndpointDescriptor {
uint8_t address = 0;
EndpointType type = EndpointType::Other;
};
struct EndpointPair {
uint8_t bulk_in = 0;
uint8_t bulk_out = 0;
};
struct DeviceInfo {
mag160c_device_info_t c_info{};
};
```
## Required public C struct and declarations
Add to include/mag160c/mag160c.h:
```c
typedef struct mag160c_device_info_t {
uint16_t vendor_id;
uint16_t product_id;
uint8_t bus;
uint8_t address;
uint8_t interface_number;
uint8_t bulk_in_endpoint;
uint8_t bulk_out_endpoint;
char product[128];
char manufacturer[128];
char serial[128];
} mag160c_device_info_t;
MAG160C_API mag160c_error_t mag160c_list_devices(
mag160c_context_t* ctx,
mag160c_device_info_t** out_devices,
size_t* out_count
);
MAG160C_API void mag160c_free_device_list(mag160c_device_info_t* devices);
```
## Required endpoint behavior
- is_in_endpoint returns true when endpoint address has bit 0x80 set.
- find_bulk_pair scans endpoints and returns the first bulk IN and first bulk OUT.
- find_bulk_pair returns false if either direction is absent.
- find_bulk_pair returns false for null out pointer.
## Required list_devices behavior
- If out is null, return MAG160C_ERR_INVALID_ARGUMENT.
- If MAG160C_HAS_LIBUSB is 0, return MAG160C_ERR_UNSUPPORTED and set last_error explaining libusb was not available at build time.
- If MAG160C_HAS_LIBUSB is 1 and no matching device is present, return MAG160C_OK and an empty vector.
- Only match VID 0x833C and PID 0x0001 initially.
- If libusb is enabled, inspect config descriptors and report the first interface/altsetting with both bulk IN and bulk OUT.
- Avoid including libusb.h unless MAG160C_HAS_LIBUSB is true so direct no-libusb builds compile.
## Required C ABI behavior
- mag160c_list_devices requires ctx, out_devices, and out_count.
- On invalid arguments, return MAG160C_ERR_INVALID_ARGUMENT.
- Initialize *out_devices to null and *out_count to 0 before enumeration.
- On empty device list, return MAG160C_OK with null devices and count 0.
- On non-empty list, allocate an array with new[] and copy mag160c_device_info_t values.
- mag160c_free_device_list deletes the array and accepts null.
## Required tests
Create tests/cpp/test_device_model.cpp:
- endpoints {0x01 interrupt, 0x82 bulk, 0x03 bulk} returns bulk_in 0x82 and bulk_out 0x03.
- endpoints {0x82 bulk} returns false because bulk OUT is absent.
- optional: null out pointer returns false.
Extend tests/cpp/test_c_api.cpp if convenient:
- mag160c_list_devices(nullptr, nullptr, nullptr) returns MAG160C_ERR_INVALID_ARGUMENT.
- with a valid context and MAG160C_HAS_LIBUSB=0, mag160c_list_devices returns MAG160C_ERR_UNSUPPORTED, devices remains null, count remains 0.
## CMake update
- Add src/core/device.cpp to mag160c_core sources.
- Add test_device_model executable linked to mag160c_core.
- Give test_device_model access to src private headers.
- Register test_device_model with CTest.
## Verification
First try:
```powershell
cmake --build build
ctest --test-dir build --output-on-failure
```
If CMake is unavailable, run:
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\core\device.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task4_c_api_test.exe
.\build_task4_c_api_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\core\device.cpp src\c_api.cpp tests\cpp\test_device_model.cpp -o build_task4_device_model_test.exe
.\build_task4_device_model_test.exe
```
Expected: both executables exit 0.
## Progress checkpoint
Append this line to progress.md after verification:
```markdown
- Implemented Task 4 USB device model, endpoint-pair selection, and C ABI device listing/freeing.
```
## Report contract
Write a report to .superpowers/sdd/task-4-report.md with:
- status: DONE, DONE_WITH_CONCERNS, NEEDS_CONTEXT, or BLOCKED
- files created/modified
- exact commands run
- test results
- concerns, if any
- self-review notes
+32
View File
@@ -0,0 +1,32 @@
# Task 4 Fix Brief: make list_devices test compatible with libusb-enabled builds
## Finding to fix
Task 4 reviewer found an Important issue:
- tests/cpp/test_c_api.cpp unconditionally expects mag160c_list_devices to return MAG160C_ERR_UNSUPPORTED.
- That is correct only when MAG160C_HAS_LIBUSB=0.
- When CMake finds libusb and builds with MAG160C_HAS_LIBUSB=1, the Task 4 spec requires no matching device to return MAG160C_OK with an empty list, and matching devices may return MAG160C_OK with a non-empty list.
- The test target currently does not receive MAG160C_HAS_LIBUSB from CMake, so it cannot conditionalize correctly.
## Required fix
- In tests/cpp/test_c_api.cpp, make the list_devices expectation conditional on MAG160C_HAS_LIBUSB.
- In no-libusb builds, continue to assert MAG160C_ERR_UNSUPPORTED, null devices, count 0, and last_error contains libusb.
- In libusb-enabled builds, assert MAG160C_OK, and always call mag160c_free_device_list(devices). Do not require count to be zero because hardware may be attached.
- In CMakeLists.txt, propagate MAG160C_HAS_LIBUSB=1 or 0 to test targets that need it, at least test_c_api.
## Verification
Run direct no-libusb verification:
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\core\device.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task4_c_api_test.exe
.\build_task4_c_api_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\core\device.cpp src\c_api.cpp tests\cpp\test_device_model.cpp -o build_task4_device_model_test.exe
.\build_task4_device_model_test.exe
```
## Report contract
Append a fix report section to .superpowers/sdd/task-4-report.md and return status plus one-line test summary.
+70
View File
@@ -0,0 +1,70 @@
# Task 4 Report
status: DONE_WITH_CONCERNS
## Files created/modified
- CMakeLists.txt
- include/mag160c/mag160c.h
- src/core/device.hpp
- src/core/device.cpp
- src/c_api.cpp
- tests/cpp/test_c_api.cpp
- tests/cpp/test_device_model.cpp
- progress.md
## Commands run
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\core\device.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task4_c_api_test.exe
.\build_task4_c_api_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\core\device.cpp src\c_api.cpp tests\cpp\test_device_model.cpp -o build_task4_device_model_test.exe
.\build_task4_device_model_test.exe
```
## Test results
- Direct MinGW C++17 compile/run verification passed for test_c_api.
- Direct MinGW C++17 compile/run verification passed for test_device_model.
## Concerns
- CMake/CTest verification remains unavailable because cmake is not installed or not on PATH.
- libusb-enabled enumeration path is implemented under conditional compilation but not compiled in this environment because MAG160C_HAS_LIBUSB=0.
- Generated local executables build_task4_c_api_test.exe and build_task4_device_model_test.exe exist from substitute verification.
## Self-review notes
- Endpoint pair selection returns first bulk IN and first bulk OUT.
- No-libusb list_devices path returns MAG160C_ERR_UNSUPPORTED and clears output pointers/count through the C ABI.
- C ABI device list allocation uses new[] and free uses delete[].
- Public device info struct uses only C-compatible fields.
## Fix report: libusb-enabled list_devices test compatibility
status: DONE
### Files modified
- CMakeLists.txt
- tests/cpp/test_c_api.cpp
### Commands run
```powershell
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\core\device.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task4_c_api_test.exe
.\build_task4_c_api_test.exe
g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\core\device.cpp src\c_api.cpp tests\cpp\test_device_model.cpp -o build_task4_device_model_test.exe
.\build_task4_device_model_test.exe
```
### Test results
- Direct MinGW C++17 compile/run verification passed for test_c_api.
- Direct MinGW C++17 compile/run verification passed for test_device_model.
### Self-review
- test_c_api now expects MAG160C_ERR_UNSUPPORTED only when MAG160C_HAS_LIBUSB is 0.
- test_c_api accepts MAG160C_OK and frees any returned device list when MAG160C_HAS_LIBUSB is 1.
- CMake now propagates MAG160C_HAS_LIBUSB to test_c_api via MAG160C_HAS_LIBUSB_VALUE.