Files
IR-protocol/IR_Decoder.cpp
DashyFox fc3510c425 fix(rx): guard null encoder in auto-accept (B2), init accept fields (B4), fix dataBuffer OOB off-by-one (B3)
B2 (HIGH): IR_Decoder::_tick — encoder->sendAccept() без null-check; у Car decoder создан с encoder==nullptr → HardFault при приёме IR_MSG_DATA_ACCEPT. Добавлен guard.
B4 (MEDIUM): acceptSendTimer/isWaitingAcceptSend/addrAcceptSendTo/acceptCustomByte не инициализировались → мусор мог спонтанно дёрнуть sendAccept. Дефолты в .h.
B3 (HIGH): writeToBuffer — '>' вместо '>=' → при i_dataBuffer==dataByteSizeMax*8 (304) запись dataBuffer[38] за границей 38-байтового массива (packSize==0 runaway). Валидные кадры закрываются при <=248, не затронуты.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:34:12 +03:00

177 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "IR_Decoder.h"
#if defined(ARDUINO_ARCH_STM32) && !defined(HAL_EXTI_MODULE_DISABLED)
#include "Arduino.h"
/* NVIC_SetPriority — CMSIS, как в IR_Encoder::begin и Car.ino (без HAL-заголовка yyxx). */
/** NVIC для линии EXTI пина (как в Arduino STM32 SrcWrapper interrupt.cpp). */
static IRQn_Type ir_decoder_exti_irqn_for_pin(uint8_t arduino_pin)
{
#if defined(STM32C0xx) || defined(STM32F0xx) || defined(STM32G0xx) || defined(STM32L0xx)
(void)arduino_pin;
return (IRQn_Type)(-1);
#else
const PinName p = digitalPinToPinName(arduino_pin);
if (p == NC) {
return (IRQn_Type)(-1);
}
const uint16_t pinmask = STM_GPIO_PIN(p);
uint8_t id = 0U;
uint16_t pm = pinmask;
while (pm != 0x0001U) {
pm = (uint16_t)(pm >> 1U);
id++;
}
#if defined(STM32H5xx) || defined(STM32MP1xx) || defined(STM32L5xx) || defined(STM32U5xx) || defined(STM32WBAxx)
static const IRQn_Type exti_irqnb[16] = {
EXTI0_IRQn, EXTI1_IRQn, EXTI2_IRQn, EXTI3_IRQn, EXTI4_IRQn, EXTI5_IRQn, EXTI6_IRQn,
EXTI7_IRQn, EXTI8_IRQn, EXTI9_IRQn, EXTI10_IRQn, EXTI11_IRQn,
EXTI12_IRQn, EXTI13_IRQn, EXTI14_IRQn, EXTI15_IRQn};
#else
static const IRQn_Type exti_irqnb[16] = {
EXTI0_IRQn, EXTI1_IRQn, EXTI2_IRQn, EXTI3_IRQn, EXTI4_IRQn,
EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn,
EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn,
EXTI15_10_IRQn, EXTI15_10_IRQn};
#endif
if (id < 16U) {
return exti_irqnb[id];
}
return (IRQn_Type)(-1);
#endif
}
static void ir_decoder_apply_rx_exti_nvic(uint8_t arduino_pin, uint32_t preempt)
{
const IRQn_Type irqn = ir_decoder_exti_irqn_for_pin(arduino_pin);
if ((int)irqn < 0) {
return;
}
#if !defined(STM32C0xx) && !defined(STM32F0xx) && !defined(STM32G0xx) && !defined(STM32L0xx)
NVIC_SetPriority(irqn, preempt);
#endif
}
void IR_Decoder::setReceiveExtiPreemptPriority(uint32_t preempt)
{
rxExtiPreemptConfigured_ = true;
rxExtiPreemptValue_ = preempt;
if (extiEnabled_) {
ir_decoder_apply_rx_exti_nvic(pin, preempt);
}
}
#endif /* ARDUINO_ARCH_STM32 && !HAL_EXTI_MODULE_DISABLED */
std::list<IR_Decoder *> &IR_Decoder::get_dec_list() // определение функции
{
static std::list<IR_Decoder *> dec_list; // статическая локальная переменная
return dec_list; // возвращается ссылка на переменную
}
// IR_Decoder::IR_Decoder() {};
IR_Decoder::IR_Decoder(const uint8_t pin, uint16_t addr, IR_Encoder *encPair, bool enableOnConstruct)
: IR_DecoderRaw(pin, addr, encPair)
{
get_dec_list().push_back(this);
if (enableOnConstruct) {
enable();
}
};
void IR_Decoder::enable()
{
auto &dec_list = get_dec_list();
if (std::find(dec_list.begin(), dec_list.end(), this) == dec_list.end())
{
dec_list.push_back(this);
}
pinMode(pin, INPUT_PULLUP);
attachInterrupt(pin, (*this)(), CHANGE);
extiEnabled_ = true;
#if defined(ARDUINO_ARCH_STM32) && !defined(HAL_EXTI_MODULE_DISABLED)
if (rxExtiPreemptConfigured_) {
ir_decoder_apply_rx_exti_nvic(pin, rxExtiPreemptValue_);
}
#endif
}
void IR_Decoder::disable()
{
extiEnabled_ = false;
detachInterrupt(pin);
pinMode(pin, INPUT);
auto &dec_list = get_dec_list();
auto it = std::find(dec_list.begin(), dec_list.end(), this);
if (it != dec_list.end())
{
dec_list.erase(it);
}
}
std::function<void()> IR_Decoder::operator()()
{
return std::bind(&IR_Decoder::isr, this);
}
IR_Decoder::~IR_Decoder()
{
IR_Decoder::get_dec_list().remove(this);
}
void IR_Decoder::tick()
{
for (const auto &element : IR_Decoder::get_dec_list())
{
element->_tick();
}
}
void IR_Decoder::_tick()
{
IR_DecoderRaw::tick();
if (availableRaw())
{
isWaitingAcceptSend = false;
switch (packInfo.buffer[0] >> 5 & IR_MASK_MSG_TYPE)
{
case IR_MSG_DATA_ACCEPT:
case IR_MSG_DATA_NOACCEPT:
gotData.set(&packInfo, id);
break;
case IR_MSG_BACK:
case IR_MSG_BACK_TO:
gotBackData.set(&packInfo, id);
break;
case IR_MSG_REQUEST:
gotRequest.set(&packInfo, id);
break;
case IR_MSG_ACCEPT:
gotAccept.set(&packInfo, id);
break;
default:
break;
}
if (gotData.isAvailable && (gotData.getMsgType() == IR_MSG_DATA_ACCEPT))
{
acceptSendTimer = millis();
addrAcceptSendTo = gotData.getAddrFrom();
acceptCustomByte = crc8(gotData.getDataPrt(), 0, gotData.getDataSize(), poly1);
if (addrAcceptSendTo && addrAcceptSendTo < IR_Broadcast)
isWaitingAcceptSend = true;
}
gotRaw.set(&packInfo, id);
}
if (isWaitingAcceptSend && millis() - acceptSendTimer > acceptDelay)
{
if (encoder != nullptr) encoder->sendAccept(addrAcceptSendTo, acceptCustomByte); // B2: у Car decoder без парного энкодера (nullptr) → был HardFault
isWaitingAcceptSend = false;
}
}
bool IR_Decoder::isReceive(uint8_t type) {
return (msgTypeReceive & 0b11111000) && ((msgTypeReceive & IR_MASK_MSG_TYPE) == type);
}