Checkpoint EEPROM, firmware analysis and instrument protocol updates

This commit is contained in:
2026-10-08 21:51:44 +03:00
parent b0c0c05cfa
commit fe9b5a246e
24 changed files with 848 additions and 62 deletions

View File

@@ -0,0 +1,44 @@
/* Private PM35 FC03 EEPROM window; not standard Modbus register quantities.
* AT25256B keeps its existing 0x8000..0xBFFF / count 1..120 requests.
* M95M02 uses four 64-KiB windows: count bit15 marks the profile, bits9:8
* select bank 0..3, bits7:0 carry word count. Address 0x8000..0xFFFF is
* the word offset within a bank. Every request is self-contained; no global
* bank register can be changed by another client. Old firmware rejects the
* marked quantity. Neither profile silently reads the other memory type.
*/
#ifndef BALSAM167_EEPROM_BLOCK_H
#define BALSAM167_EEPROM_BLOCK_H
#include "balsam167_eeprom_layout.h"
#define BALSAM167_EEPROM_RTU_BASE 0x8000U
#define BALSAM167_EEPROM_BLOCK_WORDS 120U
#define BALSAM167_EEPROM_M95_MARKER 0x8000U
#define BALSAM167_EEPROM_WRITE_M95 0x44U
static unsigned long balsam167_eeprom_capacity(unsigned profile)
{
return profile == 0 ? 32768UL : (profile == 1 ? 262144UL : 0UL);
}
static unsigned long balsam167_eeprom_block_word(unsigned address, unsigned count)
{
return ((unsigned long)(count & 0x0300U) << 7) + (address - 0x8000U);
}
static unsigned balsam167_eeprom_block_valid_profile(unsigned fn, unsigned address,
unsigned count, unsigned profile)
{
unsigned n = count & 255U;
unsigned long word;
if (fn != 3 || address < 0x8000U || !n || n > 120U) return 0;
if (profile == 0) return count == n && address < 0xC000U && n <= 0xC000U-address;
if (profile != 1 || (count & 0xFC00U) != 0x8000U) return 0;
word = balsam167_eeprom_block_word(address, count);
return n <= 0x10000UL-address && word+n <= 131072UL;
}
static unsigned balsam167_eeprom_block_valid(unsigned fn, unsigned address, unsigned count)
{
#if EEPROM_M95M02
return balsam167_eeprom_block_valid_profile(fn, address, count, 1);
#else
return fn == 3 && address >= 0x8000U && address < 0xC000U &&
count && count <= 120U && count <= 0xC000U-address;
#endif
}
#endif

View File

@@ -5,8 +5,20 @@
*/
#ifndef BALSAM167_EEPROM_LAYOUT_H
#define BALSAM167_EEPROM_LAYOUT_H
#define BALSAM167_EEPROM_BYTES 0x8000
/* EEPROM_M95M02 is selected by the board configuration, never autodetected
* by writing test patterns. Keep numeric constants usable by the TI linker. */
#define BALSAM167_AT25256_BYTES 0x8000
#define BALSAM167_M95M02_BYTES 0x40000
#ifndef EEPROM_M95M02
#define EEPROM_M95M02 0
#endif
#if EEPROM_M95M02
#define BALSAM167_EEPROM_BYTES BALSAM167_M95M02_BYTES
#define BALSAM167_EEPROM_PAGE_BYTES 256
#else
#define BALSAM167_EEPROM_BYTES BALSAM167_AT25256_BYTES
#define BALSAM167_EEPROM_PAGE_BYTES 64
#endif
#define BALSAM167_EEPROM_PARAM_WORDS 129
#define BALSAM167_EEPROM_WORDS (BALSAM167_EEPROM_BYTES / 2)
/* Preserve the historical end-exclusive parameter boundary. The final word

View File

@@ -91,6 +91,16 @@ PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_request(const void *state,
PCAN_ABI_API int pcan_abi_periph28335_eeprom_accept(void *state,
const uint8_t *reply, size_t size, uint8_t *output, size_t capacity);
PCAN_ABI_API uint32_t pcan_abi_periph28335_eeprom_capacity(void);
PCAN_ABI_API uint32_t pcan_abi_periph28335_eeprom_capacity_profile(unsigned profile);
PCAN_ABI_API int pcan_abi_periph28335_eeprom_begin_profile(void *r, uint32_t c, uint32_t a, uint32_t n, unsigned profile);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_block_expected_profile(const void *r, unsigned profile);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_block_request_profile(const void *r, uint8_t *o, size_t n, unsigned profile);
PCAN_ABI_API int pcan_abi_periph28335_eeprom_block_accept_profile(void *r, const uint8_t *p, size_t n, uint8_t *o, size_t cap, unsigned profile);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_write_profile(uint32_t controller, uint32_t destination, uint32_t source, uint32_t length, unsigned profile, uint8_t *out, size_t cap);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_block_expected(const void *state);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_block_request(const void *state, uint8_t *out, size_t cap);
PCAN_ABI_API int pcan_abi_periph28335_eeprom_block_accept(void *state, const uint8_t *reply, size_t size, uint8_t *out, size_t cap);
PCAN_ABI_API size_t pcan_abi_periph28335_append_crc(
const uint8_t *payload, size_t payload_size,
uint8_t *output, size_t output_size);

View File

@@ -50,6 +50,22 @@ size_t periph28335_eeprom_request(const periph28335_eeprom_reader *reader,
int periph28335_eeprom_accept(periph28335_eeprom_reader *reader,
const uint8_t *reply, size_t size, uint8_t *output, size_t capacity);
/* FC03 block reader uses the same state/physical byte range as legacy PEEK.
* expected() is the exact response length. No state advance on CRC/size error. */
size_t periph28335_eeprom_block_expected(const periph28335_eeprom_reader *r);
size_t periph28335_eeprom_block_request(const periph28335_eeprom_reader *r, uint8_t *out, size_t cap);
int periph28335_eeprom_block_accept(periph28335_eeprom_reader *r,
const uint8_t *reply, size_t size, uint8_t *out, size_t cap);
/* Profile 0 = AT25256B, 1 = M95M02; stable additional API for every host port.
* State remains four uint32_t fields. M95 requires block mode, no PEEK fallback. */
uint32_t periph28335_eeprom_capacity_profile(unsigned profile);
int periph28335_eeprom_begin_profile(periph28335_eeprom_reader *r, uint32_t c, uint32_t a, uint32_t n, unsigned profile);
size_t periph28335_eeprom_block_expected_profile(const periph28335_eeprom_reader *r, unsigned profile);
size_t periph28335_eeprom_block_request_profile(const periph28335_eeprom_reader *r, uint8_t *o, size_t n, unsigned profile);
int periph28335_eeprom_block_accept_profile(periph28335_eeprom_reader *r, const uint8_t *p, size_t n, uint8_t *o, size_t cap, unsigned profile);
size_t periph28335_eeprom_write_profile(uint32_t controller, uint32_t destination, uint32_t source, uint32_t length, unsigned profile, uint8_t *out, size_t cap);
typedef enum {
PERIPH28335_OK = 0,
PERIPH28335_ERROR_ARGUMENT = -1,