建立 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
+17
View File
@@ -0,0 +1,17 @@
#include "core/context.hpp"
#ifndef MAG160C_HAS_LIBUSB
#define MAG160C_HAS_LIBUSB 0
#endif
namespace mag160c::core {
Context::Context()
: has_libusb_(MAG160C_HAS_LIBUSB != 0) {}
bool Context::has_libusb() const noexcept {
return has_libusb_;
}
} // namespace mag160c::core
+19
View File
@@ -0,0 +1,19 @@
#ifndef MAG160C_CORE_CONTEXT_HPP
#define MAG160C_CORE_CONTEXT_HPP
namespace mag160c::core {
class Context {
public:
Context();
bool has_libusb() const noexcept;
private:
bool has_libusb_;
};
} // namespace mag160c::core
#endif
+185
View File
@@ -0,0 +1,185 @@
#include "core/device.hpp"
#include "core/error.hpp"
#include <cstring>
#if MAG160C_HAS_LIBUSB
#include <libusb.h>
#endif
namespace mag160c::core {
bool is_in_endpoint(uint8_t address) {
return (address & 0x80U) != 0;
}
bool find_bulk_pair(const std::vector<EndpointDescriptor>& endpoints, EndpointPair* out) {
if (out == nullptr) {
return false;
}
EndpointPair pair{};
for (const EndpointDescriptor& endpoint : endpoints) {
if (endpoint.type != EndpointType::Bulk) {
continue;
}
if (is_in_endpoint(endpoint.address) && pair.bulk_in == 0) {
pair.bulk_in = endpoint.address;
} else if (!is_in_endpoint(endpoint.address) && pair.bulk_out == 0) {
pair.bulk_out = endpoint.address;
}
}
if (pair.bulk_in == 0 || pair.bulk_out == 0) {
return false;
}
*out = pair;
return true;
}
#if MAG160C_HAS_LIBUSB
namespace {
EndpointType endpoint_type_from_libusb(uint8_t attributes) {
switch (attributes & LIBUSB_TRANSFER_TYPE_MASK) {
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
return EndpointType::Isochronous;
case LIBUSB_TRANSFER_TYPE_BULK:
return EndpointType::Bulk;
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
return EndpointType::Interrupt;
default:
return EndpointType::Other;
}
}
void copy_string_descriptor(
libusb_device_handle* handle,
uint8_t index,
char* out,
size_t out_size
) {
if (out == nullptr || out_size == 0) {
return;
}
out[0] = '\0';
if (handle == nullptr || index == 0) {
return;
}
unsigned char buffer[128] = {};
const int rc = libusb_get_string_descriptor_ascii(handle, index, buffer, sizeof(buffer));
if (rc <= 0) {
return;
}
const size_t n = static_cast<size_t>(rc) < out_size - 1 ? static_cast<size_t>(rc) : out_size - 1;
std::memcpy(out, buffer, n);
out[n] = '\0';
}
} // namespace
#endif
mag160c_error_t list_devices(std::vector<DeviceInfo>* out) {
if (out == nullptr) {
set_last_error("list_devices: out must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
out->clear();
#if !MAG160C_HAS_LIBUSB
set_last_error("list_devices: libusb-1.0 was not available at build time");
return MAG160C_ERR_UNSUPPORTED;
#else
libusb_context* ctx = nullptr;
int rc = libusb_init(&ctx);
if (rc != 0) {
set_last_error("list_devices: libusb_init failed");
return MAG160C_ERR_USB;
}
libusb_device** list = nullptr;
const ssize_t count = libusb_get_device_list(ctx, &list);
if (count < 0) {
libusb_exit(ctx);
set_last_error("list_devices: libusb_get_device_list failed");
return MAG160C_ERR_USB;
}
for (ssize_t i = 0; i < count; ++i) {
libusb_device* device = list[i];
libusb_device_descriptor desc{};
if (libusb_get_device_descriptor(device, &desc) != 0) {
continue;
}
if (desc.idVendor != MAG_IR_VENDOR_ID || desc.idProduct != MAG_IR_PRODUCT_ID) {
continue;
}
libusb_config_descriptor* config = nullptr;
if (libusb_get_active_config_descriptor(device, &config) != 0 || config == nullptr) {
continue;
}
bool added = false;
for (uint8_t iface_index = 0; iface_index < config->bNumInterfaces && !added; ++iface_index) {
const libusb_interface& iface = config->interface[iface_index];
for (int alt_index = 0; alt_index < iface.num_altsetting && !added; ++alt_index) {
const libusb_interface_descriptor& alt = iface.altsetting[alt_index];
std::vector<EndpointDescriptor> endpoints;
endpoints.reserve(alt.bNumEndpoints);
for (uint8_t ep_index = 0; ep_index < alt.bNumEndpoints; ++ep_index) {
const libusb_endpoint_descriptor& ep = alt.endpoint[ep_index];
endpoints.push_back({ep.bEndpointAddress, endpoint_type_from_libusb(ep.bmAttributes)});
}
EndpointPair pair{};
if (!find_bulk_pair(endpoints, &pair)) {
continue;
}
DeviceInfo info{};
info.c_info.vendor_id = desc.idVendor;
info.c_info.product_id = desc.idProduct;
info.c_info.bus = static_cast<uint8_t>(libusb_get_bus_number(device));
info.c_info.address = static_cast<uint8_t>(libusb_get_device_address(device));
info.c_info.interface_number = alt.bInterfaceNumber;
info.c_info.bulk_in_endpoint = pair.bulk_in;
info.c_info.bulk_out_endpoint = pair.bulk_out;
libusb_device_handle* handle = nullptr;
if (libusb_open(device, &handle) == 0) {
copy_string_descriptor(handle, desc.iProduct, info.c_info.product, sizeof(info.c_info.product));
copy_string_descriptor(
handle,
desc.iManufacturer,
info.c_info.manufacturer,
sizeof(info.c_info.manufacturer)
);
copy_string_descriptor(handle, desc.iSerialNumber, info.c_info.serial, sizeof(info.c_info.serial));
libusb_close(handle);
}
out->push_back(info);
added = true;
}
}
libusb_free_config_descriptor(config);
}
libusb_free_device_list(list, 1);
libusb_exit(ctx);
clear_last_error();
return MAG160C_OK;
#endif
}
} // namespace mag160c::core
+41
View File
@@ -0,0 +1,41 @@
#ifndef MAG160C_CORE_DEVICE_HPP
#define MAG160C_CORE_DEVICE_HPP
#include "mag160c/mag160c.h"
#include <cstdint>
#include <vector>
namespace mag160c::core {
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{};
};
bool is_in_endpoint(uint8_t address);
bool find_bulk_pair(const std::vector<EndpointDescriptor>& endpoints, EndpointPair* out);
mag160c_error_t list_devices(std::vector<DeviceInfo>* out);
} // namespace mag160c::core
#endif
+51
View File
@@ -0,0 +1,51 @@
#include "core/error.hpp"
#include <utility>
namespace mag160c::core {
namespace {
thread_local std::string g_last_error;
} // namespace
const char* error_name(mag160c_error_t code) noexcept {
switch (code) {
case MAG160C_OK:
return "MAG160C_OK";
case MAG160C_ERR_INVALID_ARGUMENT:
return "MAG160C_ERR_INVALID_ARGUMENT";
case MAG160C_ERR_NO_DEVICE:
return "MAG160C_ERR_NO_DEVICE";
case MAG160C_ERR_PERMISSION:
return "MAG160C_ERR_PERMISSION";
case MAG160C_ERR_USB:
return "MAG160C_ERR_USB";
case MAG160C_ERR_TIMEOUT:
return "MAG160C_ERR_TIMEOUT";
case MAG160C_ERR_CHECKSUM:
return "MAG160C_ERR_CHECKSUM";
case MAG160C_ERR_PROTOCOL_UNKNOWN:
return "MAG160C_ERR_PROTOCOL_UNKNOWN";
case MAG160C_ERR_UNSUPPORTED:
return "MAG160C_ERR_UNSUPPORTED";
case MAG160C_ERR_INTERNAL:
return "MAG160C_ERR_INTERNAL";
default:
return "MAG160C_ERR_UNKNOWN_CODE";
}
}
const char* last_error() noexcept {
return g_last_error.c_str();
}
void clear_last_error() {
g_last_error.clear();
}
void set_last_error(std::string message) {
g_last_error = std::move(message);
}
} // namespace mag160c::core
+18
View File
@@ -0,0 +1,18 @@
#ifndef MAG160C_CORE_ERROR_HPP
#define MAG160C_CORE_ERROR_HPP
#include "mag160c/mag160c.h"
#include <string>
namespace mag160c::core {
const char* error_name(mag160c_error_t code) noexcept;
const char* last_error() noexcept;
void clear_last_error();
void set_last_error(std::string message);
} // namespace mag160c::core
#endif
@@ -0,0 +1,65 @@
#include "core/ir_device.hpp"
#include "core/error.hpp"
#include <cstring>
namespace mag160c::core {
namespace {
void copy_literal(char* out, size_t out_size, const char* value) {
if (out == nullptr || out_size == 0) {
return;
}
std::strncpy(out, value, out_size - 1);
out[out_size - 1] = '\0';
}
} // namespace
IrDevice::IrDevice() {
info_.width = 160;
info_.height = 120;
info_.output_width = 160;
info_.output_height = 120;
info_.max_fps = 25;
info_.current_fps = 0;
copy_literal(info_.name, sizeof(info_.name), "MAG160C");
copy_literal(info_.type, sizeof(info_.type), "vendor-bulk-ir");
}
const mag160c_ir_info_t& IrDevice::info() const noexcept {
return info_;
}
mag160c_error_t IrDevice::trigger_ffc() const {
set_last_error(
"IR FFC protocol recovered from libmagcore.so.2.1.1: write {u32 0x6bb6b672, u32 param} "
"on bulk endpoint OUT 0x03, then read response on IN 0x82 (0x1000 max, 2000 ms timeout); "
"live USB transport is not available in this build"
);
return MAG160C_ERR_UNSUPPORTED;
}
mag160c_error_t IrDevice::read_raw_once(
unsigned char* /*out_bytes*/,
size_t /*out_capacity*/,
size_t* out_size,
int /*timeout_ms*/
) const {
if (out_size == nullptr) {
set_last_error("mag160c_ir_read_raw_once: out_size must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
*out_size = 0;
set_last_error(
"IR raw read protocol recovered: start stream with {0x6bb6b673} on OUT 0x03, then read "
"frame stream on IN 0x81 (markers 0x1bb1b11b/0x1bb1b11c, data at +0x1c, size 0x38+len); "
"live USB transport is not available in this build"
);
return MAG160C_ERR_UNSUPPORTED;
}
} // namespace mag160c::core
@@ -0,0 +1,27 @@
#ifndef MAG160C_CORE_IR_DEVICE_HPP
#define MAG160C_CORE_IR_DEVICE_HPP
#include "mag160c/mag160c.h"
namespace mag160c::core {
class IrDevice {
public:
IrDevice();
const mag160c_ir_info_t& info() const noexcept;
mag160c_error_t trigger_ffc() const;
mag160c_error_t read_raw_once(
unsigned char* out_bytes,
size_t out_capacity,
size_t* out_size,
int timeout_ms
) const;
private:
mag160c_ir_info_t info_{};
};
} // namespace mag160c::core
#endif
+107
View File
@@ -0,0 +1,107 @@
#include "core/ir_frame.hpp"
namespace mag160c::core {
mag160c_error_t parse_ir_frame(
const uint8_t* data,
size_t size,
IrFrameHeader* out_header,
const uint16_t** out_pixels
) {
if (data == nullptr || out_header == nullptr || out_pixels == nullptr) {
return MAG160C_ERR_INVALID_ARGUMENT;
}
if (size < MAG160C_IR_FRAME_DATA_OFFSET + 4) {
return MAG160C_ERR_PROTOCOL_UNKNOWN;
}
const uint32_t marker =
static_cast<uint32_t>(data[0]) |
(static_cast<uint32_t>(data[1]) << 8) |
(static_cast<uint32_t>(data[2]) << 16) |
(static_cast<uint32_t>(data[3]) << 24);
if (marker != MAG160C_IR_FRAME_MARKER) {
return MAG160C_ERR_PROTOCOL_UNKNOWN;
}
auto rd32 = [&](size_t off) {
return static_cast<uint32_t>(data[off]) |
(static_cast<uint32_t>(data[off + 1]) << 8) |
(static_cast<uint32_t>(data[off + 2]) << 16) |
(static_cast<uint32_t>(data[off + 3]) << 24);
};
IrFrameHeader h{};
h.frame_counter = rd32(4);
h.data_length = rd32(8);
h.frame_type = rd32(12);
h.period_shutter = rd32(16);
if (h.frame_type > 1) {
return MAG160C_ERR_PROTOCOL_UNKNOWN;
}
if (size < MAG160C_IR_FRAME_OVERHEAD + h.data_length) {
return MAG160C_ERR_PROTOCOL_UNKNOWN;
}
const size_t trailing_offset = MAG160C_IR_FRAME_DATA_OFFSET + h.data_length;
if (rd32(trailing_offset) != MAG160C_IR_FRAME_TRAILING_MARKER) {
return MAG160C_ERR_PROTOCOL_UNKNOWN;
}
h.marker = marker;
*out_header = h;
*out_pixels = reinterpret_cast<const uint16_t*>(data + MAG160C_IR_FRAME_DATA_OFFSET);
return MAG160C_OK;
}
void calibrate_frame(
const uint16_t* frame,
const IrCalibrationTables& tables,
uint16_t* out
) {
if (frame == nullptr || tables.coeff == nullptr || tables.thresholds == nullptr ||
out == nullptr || tables.pixel_count == 0) {
return;
}
const uint32_t pixels = tables.pixel_count;
const uint32_t bands = tables.band_count == 0 ? 1 : tables.band_count;
const uint32_t search_bands = bands - 1;
for (uint32_t p = 0; p < pixels; ++p) {
int32_t diff;
if (tables.has_baseline && tables.baseline != nullptr) {
diff = static_cast<int32_t>(static_cast<int16_t>(
frame[p] - tables.baseline[p]));
} else {
diff = static_cast<int32_t>(frame[p]);
}
diff >>= 1;
uint32_t band = 0;
if (search_bands > 0) {
for (uint32_t i = 0; i < search_bands; ++i) {
if (diff <= tables.thresholds[p * search_bands + i]) {
band = i;
break;
}
band = i + 1;
}
}
const size_t entry = (static_cast<size_t>(band) * pixels + p) * 2;
const uint32_t coeff = tables.coeff[entry];
const uint32_t offset = tables.coeff[entry + 1];
int32_t v = static_cast<int32_t>(offset) + ((diff * static_cast<int32_t>(coeff)) >> 12);
if (v < 0) {
v = 0;
}
if (v > 0xffff) {
v = 0xffff;
}
out[p] = static_cast<uint16_t>(v);
}
}
} // namespace mag160c::core
+57
View File
@@ -0,0 +1,57 @@
#ifndef MAG160C_CORE_IR_FRAME_HPP
#define MAG160C_CORE_IR_FRAME_HPP
#include "mag160c/mag160c.h"
#include <cstddef>
#include <cstdint>
namespace mag160c::core {
struct IrFrameHeader {
uint32_t marker;
uint32_t frame_counter;
uint32_t data_length;
uint32_t frame_type;
uint32_t period_shutter;
};
struct IrCalibrationTables {
const int16_t* thresholds; /* per-pixel band thresholds, [bands] per pixel */
const uint16_t* coeff; /* per-pixel per-band {coeff, offset} pairs, 2*bands per pixel */
uint32_t pixel_count; /* 160*120 = 19200 */
uint32_t band_count; /* thresholds per pixel (band_count-1 searched) */
const uint16_t* baseline; /* optional baseline frame (may be null) */
bool has_baseline;
};
/*
* Parse a frame stream chunk recovered from libmagcore.so.2.1.1:
* marker 0x1bb1b11b @0, frame counter @4, data length @8, type @0xc, shutter @0x10,
* pixel data @0x1c, trailing marker 0x1bb1b11c @0x1c+data_length, total 0x38+data_length.
* Returns MAG160C_OK when a complete, valid frame is present.
*/
mag160c_error_t parse_ir_frame(
const uint8_t* data,
size_t size,
IrFrameHeader* out_header,
const uint16_t** out_pixels
);
/*
* Recovered CFunctions::Calibration piecewise-linear map (libcoresdk.so 0x6a41c):
* diff = (int16)(frame[p] - baseline[p]) >> 1
* band = first index i where diff <= thresholds[p*bands+i] (linear scan, max bands-1)
* v = coeff[(band*pixels + p)*2 + 1] + ((diff * coeff[(band*pixels + p)*2]) >> 12)
* out[p] = clamp(v, 0, 0xffff)
* When no baseline is set, diff = frame[p] >> 1.
*/
void calibrate_frame(
const uint16_t* frame,
const IrCalibrationTables& tables,
uint16_t* out
);
} // namespace mag160c::core
#endif
@@ -0,0 +1,70 @@
#include "core/tcm_device.hpp"
#include <algorithm>
namespace mag160c::core {
namespace {
constexpr uint8_t MAIN_TCM = 0x02;
constexpr uint8_t SUB_ROTATE = 0x77;
constexpr uint8_t SUB_LIGHT_STEADY = 0x31;
constexpr uint8_t SUB_LIGHT_BLINK = 0x32;
constexpr uint8_t SUB_LIGHT_BREATH = 0x33;
uint8_t light_subcommand(mag160c_tcm_light_mode_t mode) {
switch (mode) {
case MAG160C_TCM_LIGHT_STEADY:
return SUB_LIGHT_STEADY;
case MAG160C_TCM_LIGHT_BLINK:
return SUB_LIGHT_BLINK;
case MAG160C_TCM_LIGHT_BREATH:
return SUB_LIGHT_BREATH;
}
return SUB_LIGHT_STEADY;
}
std::vector<uint8_t> light_payload(mag160c_tcm_light_color_t color) {
switch (color) {
case MAG160C_TCM_LIGHT_OFF:
return {0x00, 0x00, 0x00, 0x00};
case MAG160C_TCM_LIGHT_RED:
return {0x01, 0xff, 0x00, 0x00};
case MAG160C_TCM_LIGHT_GREEN:
return {0x01, 0x00, 0xff, 0x00};
case MAG160C_TCM_LIGHT_BLUE:
return {0x01, 0x00, 0x00, 0xff};
case MAG160C_TCM_LIGHT_YELLOW:
return {0x01, 0xff, 0xff, 0x00};
}
return {0x00, 0x00, 0x00, 0x00};
}
} // namespace
TcmCommandBuilder::TcmCommandBuilder() : frame_id_(1) {}
uint16_t TcmCommandBuilder::next_frame_id() {
const uint16_t current = frame_id_;
++frame_id_;
if (frame_id_ >= 0x8000) {
frame_id_ = 1;
}
return current;
}
std::vector<uint8_t> TcmCommandBuilder::build_rotate_frame(int angle) {
angle = std::clamp(angle, -128, 128);
const uint8_t direction = angle < 0 ? 0x01 : 0x00;
const uint8_t magnitude = static_cast<uint8_t>(angle < 0 ? -angle : angle);
return encode_tcm_frame(MAIN_TCM, SUB_ROTATE, next_frame_id(), {direction, magnitude});
}
std::vector<uint8_t> TcmCommandBuilder::build_light_frame(
mag160c_tcm_light_color_t color,
mag160c_tcm_light_mode_t mode
) {
return encode_tcm_frame(MAIN_TCM, light_subcommand(mode), next_frame_id(), light_payload(color));
}
} // namespace mag160c::core
@@ -0,0 +1,30 @@
#ifndef MAG160C_CORE_TCM_DEVICE_HPP
#define MAG160C_CORE_TCM_DEVICE_HPP
#include "core/tcm_frame.hpp"
#include "mag160c/mag160c.h"
#include <cstdint>
#include <vector>
namespace mag160c::core {
class TcmCommandBuilder {
public:
TcmCommandBuilder();
std::vector<uint8_t> build_rotate_frame(int angle);
std::vector<uint8_t> build_light_frame(
mag160c_tcm_light_color_t color,
mag160c_tcm_light_mode_t mode
);
private:
uint16_t next_frame_id();
uint16_t frame_id_;
};
} // namespace mag160c::core
#endif
+101
View File
@@ -0,0 +1,101 @@
#include "core/tcm_frame.hpp"
#include "core/error.hpp"
#include <stdexcept>
namespace mag160c::core {
namespace {
constexpr size_t MAX_TCM_PAYLOAD_SIZE = 0xffffU - 5U;
} // namespace
uint8_t checksum(const uint8_t* data, size_t begin, size_t end) {
if (data == nullptr || begin > end) {
return 0;
}
uint8_t sum = 0;
for (size_t i = begin; i < end; ++i) {
sum = static_cast<uint8_t>(sum + data[i]);
}
return sum;
}
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
) {
if (payload.size() > MAX_TCM_PAYLOAD_SIZE) {
throw std::length_error("TCM payload is too large");
}
const uint16_t body_length = static_cast<uint16_t>(payload.size() + 5U);
std::vector<uint8_t> out(static_cast<size_t>(body_length) + 4U, 0);
out[0] = 0x7e;
out[1] = static_cast<uint8_t>((body_length >> 8) & 0xff);
out[2] = static_cast<uint8_t>(body_length & 0xff);
out[3] = checksum(out.data(), 0, 3);
out[4] = main_cmd;
out[5] = sub_cmd;
out[6] = static_cast<uint8_t>((frame_id >> 8) & 0xff);
out[7] = static_cast<uint8_t>(frame_id & 0xff);
for (size_t i = 0; i < payload.size(); ++i) {
out[8 + i] = payload[i];
}
out[out.size() - 1] = checksum(out.data(), 4, out.size() - 1);
return out;
}
mag160c_error_t decode_tcm_frame(const uint8_t* data, size_t size, TcmFrame* out) {
if (data == nullptr || out == nullptr) {
set_last_error("decode_tcm_frame: data and out must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
if (size < 9) {
set_last_error("decode_tcm_frame: packet shorter than minimum TCM frame");
return MAG160C_ERR_INVALID_ARGUMENT;
}
if (data[0] != 0x7e) {
set_last_error("decode_tcm_frame: missing 0x7e header byte");
return MAG160C_ERR_CHECKSUM;
}
if (data[3] != checksum(data, 0, 3)) {
set_last_error("decode_tcm_frame: invalid header checksum");
return MAG160C_ERR_CHECKSUM;
}
const uint16_t body_length =
static_cast<uint16_t>((static_cast<uint16_t>(data[1]) << 8) | data[2]);
const size_t expected_size = static_cast<size_t>(body_length) + 4U;
if (size != expected_size) {
set_last_error("decode_tcm_frame: packet size does not match encoded body length");
return MAG160C_ERR_INVALID_ARGUMENT;
}
if (data[size - 1] != checksum(data, 4, size - 1)) {
set_last_error("decode_tcm_frame: invalid body checksum");
return MAG160C_ERR_CHECKSUM;
}
out->main_cmd = data[4];
out->sub_cmd = data[5];
out->frame_id = static_cast<uint16_t>((static_cast<uint16_t>(data[6]) << 8) | data[7]);
out->payload.assign(data + 8, data + size - 1);
clear_last_error();
return MAG160C_OK;
}
} // namespace mag160c::core
@@ -0,0 +1,30 @@
#ifndef MAG160C_CORE_TCM_FRAME_HPP
#define MAG160C_CORE_TCM_FRAME_HPP
#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);
} // namespace mag160c::core
#endif
@@ -0,0 +1,20 @@
#ifndef MAG160C_CORE_TRANSPORT_HPP
#define MAG160C_CORE_TRANSPORT_HPP
#include "mag160c/mag160c.h"
#include <cstdint>
#include <vector>
namespace mag160c::core {
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;
};
} // namespace mag160c::core
#endif