/* * Автомат адресной загрузки прошивки по ProtoCAN с сессией, контрольной суммой и A/B-слотами. * Приём блоков, проверка образа и активация разделены на этапы; операции Flash и метаданные * слотов предоставляет платформа. */ #ifndef PCAN_BOOT_H #define PCAN_BOOT_H #include #include #include /* C28x has 16-bit addressable chars; wire octets use the low eight bits. */ #if defined(__TMS320C28XX__) || defined(PCAN_BOOT_WIDE_BYTES) typedef unsigned int pcan_boot_byte_t; #else typedef uint8_t pcan_boot_byte_t; #endif #ifdef __cplusplus extern "C" { #endif #define PCAN_BOOT_VERSION 1U #define PCAN_BOOT_BLOCK_SIZE 8U #define PCAN_BOOT_BLOCK_COUNT 65536UL #define PCAN_BOOT_SLOT_SIZE (PCAN_BOOT_BLOCK_COUNT * PCAN_BOOT_BLOCK_SIZE) #define PCAN_BOOT_SLOT_NONE 0xFFU #define PCAN_BOOT_MSG_CONTROL 0x9U #define PCAN_BOOT_MSG_DATA_A 0xAU #define PCAN_BOOT_MSG_DATA_B 0xBU #define PCAN_BOOT_MSG_STATUS 0xCU #define PCAN_BOOT_MSG_DISCOVERY 0xDU #define PCAN_BOOT_ROUTE_FROM_PM 0U #define PCAN_BOOT_ROUTE_FROM_DEVICE 1U typedef enum { PCAN_BOOT_CMD_IDENTIFY = 0x01U, PCAN_BOOT_CMD_ENTER_BOOT = 0x02U, PCAN_BOOT_CMD_BEGIN_IMAGE = 0x03U, PCAN_BOOT_CMD_BEGIN_COMPAT = 0x04U, PCAN_BOOT_CMD_ERASE = 0x05U, PCAN_BOOT_CMD_VERIFY = 0x06U, PCAN_BOOT_CMD_COMMIT = 0x07U, PCAN_BOOT_CMD_CONFIRM = 0x08U, PCAN_BOOT_CMD_REBOOT = 0x09U, PCAN_BOOT_CMD_ABORT = 0x0AU, PCAN_BOOT_CMD_QUERY_PROGRESS = 0x0BU, PCAN_BOOT_CMD_PING = 0x0CU, PCAN_BOOT_CMD_CAPABILITIES = 0x0DU, PCAN_BOOT_CMD_DEVICE_NAME = 0x0EU, PCAN_BOOT_CMD_MEMORY_INFO = 0x0FU } pcan_boot_command_t; typedef enum { PCAN_BOOT_STATUS_OK = 0x00U, PCAN_BOOT_STATUS_BUSY = 0x01U, PCAN_BOOT_STATUS_INVALID_COMMAND = 0x02U, PCAN_BOOT_STATUS_WRONG_DEVICE = 0x03U, PCAN_BOOT_STATUS_WRONG_HARDWARE = 0x04U, PCAN_BOOT_STATUS_INVALID_SIZE = 0x05U, PCAN_BOOT_STATUS_CRC_ERROR = 0x06U, PCAN_BOOT_STATUS_FLASH_ERROR = 0x07U, PCAN_BOOT_STATUS_SEQUENCE_ERROR = 0x08U, PCAN_BOOT_STATUS_SIGNATURE_ERROR = 0x09U, PCAN_BOOT_STATUS_SESSION_ERROR = 0x0AU, PCAN_BOOT_STATUS_VOLTAGE_ERROR = 0x0BU, PCAN_BOOT_STATUS_INVALID_STATE = 0x0CU } pcan_boot_status_t; typedef enum { PCAN_BOOT_STATE_IDLE = 0, PCAN_BOOT_STATE_METADATA, PCAN_BOOT_STATE_READY_TO_ERASE, PCAN_BOOT_STATE_RECEIVING, PCAN_BOOT_STATE_VERIFIED, PCAN_BOOT_STATE_FAILED, PCAN_BOOT_STATE_COMMITTED } pcan_boot_state_t; typedef struct { uint32_t image_size; uint32_t image_crc32; uint32_t firmware_version; uint16_t product_type; pcan_boot_byte_t hardware_revision_min; pcan_boot_byte_t hardware_revision_max; } pcan_boot_manifest_t; typedef struct { pcan_boot_byte_t device_type; pcan_boot_byte_t device_id; uint16_t product_type; pcan_boot_byte_t hardware_revision; uint32_t firmware_version; pcan_boot_byte_t active_slot; /** 1 for an in-place/single-slot target, 2 for A/B. Zero keeps legacy A/B. */ pcan_boot_byte_t slot_count; /** Writable image capacity. Zero uses PCAN_BOOT_SLOT_SIZE. */ uint32_t max_image_size; pcan_boot_byte_t ack_window; /**< 1 — ACK каждого блока, 16 — ACK каждых 16 блоков. */ uint32_t session_timeout_ms; /**< 0 disables timeout for legacy ports. */ } pcan_boot_config_t; typedef struct { bool (*send)(void *user, uint32_t can_id, const pcan_boot_byte_t *data, pcan_boot_byte_t dlc); bool (*erase_slot)(void *user, pcan_boot_byte_t slot); bool (*write_slot)(void *user, pcan_boot_byte_t slot, uint32_t offset, const pcan_boot_byte_t *data, pcan_boot_byte_t length); bool (*authorize)(void *user, const pcan_boot_manifest_t *manifest); bool (*verify_image)(void *user, pcan_boot_byte_t slot, const pcan_boot_manifest_t *manifest); /** A/B metadata, or optional single-slot activation after verification. */ bool (*set_pending_slot)(void *user, pcan_boot_byte_t slot, const pcan_boot_manifest_t *manifest); bool (*confirm_running_slot)(void *user); void (*reboot)(void *user); /** ENTER: 0 ready, 1 deferred ACK after reset, 2 denied. NULL = legacy ready. */ unsigned int (*enter_boot)(void *user, pcan_boot_byte_t session); bool (*can_reboot)(void *user); /** Eight-octet service response, addressed by command + page. */ bool (*service_info)(void *user, pcan_boot_byte_t command, pcan_boot_byte_t page, pcan_boot_byte_t *out); /** Optional precise result, replacing legacy boolean verify_image. */ pcan_boot_status_t (*verify_status)(void *user, pcan_boot_byte_t slot, const pcan_boot_manifest_t *manifest); } pcan_boot_port_t; typedef struct { pcan_boot_config_t config; pcan_boot_port_t port; void *port_user; pcan_boot_manifest_t manifest; pcan_boot_state_t state; uint32_t running_crc32; uint32_t bytes_received; uint16_t next_block; pcan_boot_byte_t session_id; pcan_boot_byte_t target_slot; pcan_boot_byte_t last_status; bool have_image; bool have_compat; uint32_t now_ms, last_activity_ms, discovery_at; bool discovery_pending; } pcan_boot_t; /** Инициализирует экземпляр. Все обязательные callbacks должны быть заданы. */ bool pcan_boot_init(pcan_boot_t *boot, const pcan_boot_config_t *config, const pcan_boot_port_t *port, void *port_user); /** Сбрасывает незавершённую сессию, не меняя конфигурацию и callbacks. */ void pcan_boot_abort(pcan_boot_t *boot); /** * Обрабатывает один Extended CAN-кадр. * Возвращает true, если кадр принадлежал загрузочному сервису этого прибора. */ bool pcan_boot_process(pcan_boot_t *boot, uint32_t can_id, const pcan_boot_byte_t *data, pcan_boot_byte_t dlc); /** Call regularly with a wrapping monotonic millisecond clock. */ void pcan_boot_poll(pcan_boot_t *boot, uint32_t now_ms); /** Переносимые операции с 29-битным ProtoCAN ID. */ uint32_t pcan_boot_make_id(pcan_boot_byte_t priority, pcan_boot_byte_t route, pcan_boot_byte_t device_type, pcan_boot_byte_t device_id, pcan_boot_byte_t msg_type, uint16_t msg_body); pcan_boot_byte_t pcan_boot_id_route(uint32_t can_id); pcan_boot_byte_t pcan_boot_id_device_type(uint32_t can_id); pcan_boot_byte_t pcan_boot_id_device_id(uint32_t can_id); pcan_boot_byte_t pcan_boot_id_msg_type(uint32_t can_id); uint16_t pcan_boot_id_msg_body(uint32_t can_id); #ifdef __cplusplus } #endif #endif /* PCAN_BOOT_H */