Fix Memory leak in addApbChangeCallback() (#3560)
* `ledcWriteTone()` added a `apbcallback()` evertime the tone value was non zero. * `addApbChangeCallback()` did not detect duplicate callbacks. * changed the apbcallback list to a double link to support roll forward, roll back execution. This made the sequences of clock change callback start with the newest registered -> to oldest on the `before` then oldest -> newest after the clock change. This made the UART debug log output have minimal gibberish during the clock change. * change how the UART callback handled the MUTEX because if any `apbchangeCallback()` executed a `log_x()` a deadlock would occur. This fixes #3555
This commit is contained in:
parent
cec3fca4ad
commit
9ad860758c
@ -28,6 +28,7 @@
|
|||||||
#include "esp32-hal-cpu.h"
|
#include "esp32-hal-cpu.h"
|
||||||
|
|
||||||
typedef struct apb_change_cb_s {
|
typedef struct apb_change_cb_s {
|
||||||
|
struct apb_change_cb_s * prev;
|
||||||
struct apb_change_cb_s * next;
|
struct apb_change_cb_s * next;
|
||||||
void * arg;
|
void * arg;
|
||||||
apb_change_cb_t cb;
|
apb_change_cb_t cb;
|
||||||
@ -53,10 +54,20 @@ static void triggerApbChangeCallback(apb_change_ev_t ev_type, uint32_t old_apb,
|
|||||||
initApbChangeCallback();
|
initApbChangeCallback();
|
||||||
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
|
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
|
||||||
apb_change_t * r = apb_change_callbacks;
|
apb_change_t * r = apb_change_callbacks;
|
||||||
|
if( r != NULL ){
|
||||||
|
if(ev_type == APB_BEFORE_CHANGE )
|
||||||
while(r != NULL){
|
while(r != NULL){
|
||||||
r->cb(r->arg, ev_type, old_apb, new_apb);
|
r->cb(r->arg, ev_type, old_apb, new_apb);
|
||||||
r=r->next;
|
r=r->next;
|
||||||
}
|
}
|
||||||
|
else { // run backwards through chain
|
||||||
|
while(r->next != NULL) r = r->next; // find first added
|
||||||
|
while( r != NULL){
|
||||||
|
r->cb(r->arg, ev_type, old_apb, new_apb);
|
||||||
|
r=r->prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
xSemaphoreGive(apb_change_lock);
|
xSemaphoreGive(apb_change_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +79,7 @@ bool addApbChangeCallback(void * arg, apb_change_cb_t cb){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
c->next = NULL;
|
c->next = NULL;
|
||||||
|
c->prev = NULL;
|
||||||
c->arg = arg;
|
c->arg = arg;
|
||||||
c->cb = cb;
|
c->cb = cb;
|
||||||
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
|
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
|
||||||
@ -75,18 +87,20 @@ bool addApbChangeCallback(void * arg, apb_change_cb_t cb){
|
|||||||
apb_change_callbacks = c;
|
apb_change_callbacks = c;
|
||||||
} else {
|
} else {
|
||||||
apb_change_t * r = apb_change_callbacks;
|
apb_change_t * r = apb_change_callbacks;
|
||||||
if(r->cb != cb || r->arg != arg){
|
// look for duplicate callbacks
|
||||||
while(r->next){
|
while( (r != NULL ) && !((r->cb == cb) && ( r->arg == arg))) r = r->next;
|
||||||
r = r->next;
|
if (r) {
|
||||||
if(r->cb == cb && r->arg == arg){
|
log_e("duplicate func=%08X arg=%08X",c->cb,c->arg);
|
||||||
free(c);
|
free(c);
|
||||||
goto unlock_and_exit;
|
xSemaphoreGive(apb_change_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c->next = apb_change_callbacks;
|
||||||
|
apb_change_callbacks-> prev = c;
|
||||||
|
apb_change_callbacks = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r->next = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unlock_and_exit:
|
|
||||||
xSemaphoreGive(apb_change_lock);
|
xSemaphoreGive(apb_change_lock);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -95,24 +109,21 @@ bool removeApbChangeCallback(void * arg, apb_change_cb_t cb){
|
|||||||
initApbChangeCallback();
|
initApbChangeCallback();
|
||||||
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
|
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
|
||||||
apb_change_t * r = apb_change_callbacks;
|
apb_change_t * r = apb_change_callbacks;
|
||||||
|
// look for matching callback
|
||||||
|
while( (r != NULL ) && !((r->cb == cb) && ( r->arg == arg))) r = r->next;
|
||||||
if ( r == NULL ) {
|
if ( r == NULL ) {
|
||||||
|
log_e("not found func=%08X arg=%08X",cb,arg);
|
||||||
xSemaphoreGive(apb_change_lock);
|
xSemaphoreGive(apb_change_lock);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(r->cb == cb && r->arg == arg){
|
else {
|
||||||
|
// patch links
|
||||||
|
if(r->prev) r->prev->next = r->next;
|
||||||
|
else { // this is first link
|
||||||
apb_change_callbacks = r->next;
|
apb_change_callbacks = r->next;
|
||||||
|
}
|
||||||
|
if(r->next) r->next->prev = r->prev;
|
||||||
free(r);
|
free(r);
|
||||||
} else {
|
|
||||||
while(r->next && (r->next->cb != cb || r->next->arg != arg)){
|
|
||||||
r = r->next;
|
|
||||||
}
|
|
||||||
if(r->next == NULL || r->next->cb != cb || r->next->arg != arg){
|
|
||||||
xSemaphoreGive(apb_change_lock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
apb_change_t * c = r->next;
|
|
||||||
r->next = c->next;
|
|
||||||
free(c);
|
|
||||||
}
|
}
|
||||||
xSemaphoreGive(apb_change_lock);
|
xSemaphoreGive(apb_change_lock);
|
||||||
return true;
|
return true;
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
#else
|
#else
|
||||||
#define LEDC_MUTEX_LOCK() do {} while (xSemaphoreTake(_ledc_sys_lock, portMAX_DELAY) != pdPASS)
|
#define LEDC_MUTEX_LOCK() do {} while (xSemaphoreTake(_ledc_sys_lock, portMAX_DELAY) != pdPASS)
|
||||||
#define LEDC_MUTEX_UNLOCK() xSemaphoreGive(_ledc_sys_lock)
|
#define LEDC_MUTEX_UNLOCK() xSemaphoreGive(_ledc_sys_lock)
|
||||||
xSemaphoreHandle _ledc_sys_lock;
|
xSemaphoreHandle _ledc_sys_lock = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -55,18 +55,19 @@ xSemaphoreHandle _ledc_sys_lock;
|
|||||||
|
|
||||||
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
||||||
if(ev_type == APB_AFTER_CHANGE && old_apb != new_apb){
|
if(ev_type == APB_AFTER_CHANGE && old_apb != new_apb){
|
||||||
uint32_t iarg = (uint32_t)arg;
|
uint16_t iarg = *(uint16_t*)arg;
|
||||||
uint8_t chan = iarg;
|
uint8_t chan = 0;
|
||||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
|
||||||
old_apb /= 1000000;
|
old_apb /= 1000000;
|
||||||
new_apb /= 1000000;
|
new_apb /= 1000000;
|
||||||
|
while(iarg){ // run though all active channels, adjusting timing configurations
|
||||||
|
if(iarg & 1) {// this channel is active
|
||||||
|
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||||
if(LEDC_TIMER(group, timer).conf.tick_sel){
|
if(LEDC_TIMER(group, timer).conf.tick_sel){
|
||||||
LEDC_MUTEX_LOCK();
|
LEDC_MUTEX_LOCK();
|
||||||
uint32_t old_div = LEDC_TIMER(group, timer).conf.clock_divider;
|
uint32_t old_div = LEDC_TIMER(group, timer).conf.clock_divider;
|
||||||
uint32_t div_num = (new_apb * old_div) / old_apb;
|
uint32_t div_num = (new_apb * old_div) / old_apb;
|
||||||
if(div_num > LEDC_DIV_NUM_HSTIMER0_V){
|
if(div_num > LEDC_DIV_NUM_HSTIMER0_V){
|
||||||
new_apb = REF_CLK_FREQ / 1000000;
|
div_num = ((REF_CLK_FREQ /1000000) * old_div) / old_apb;
|
||||||
div_num = (new_apb * old_div) / old_apb;
|
|
||||||
if(div_num > LEDC_DIV_NUM_HSTIMER0_V) {
|
if(div_num > LEDC_DIV_NUM_HSTIMER0_V) {
|
||||||
div_num = LEDC_DIV_NUM_HSTIMER0_V;//lowest clock possible
|
div_num = LEDC_DIV_NUM_HSTIMER0_V;//lowest clock possible
|
||||||
}
|
}
|
||||||
@ -77,6 +78,13 @@ static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb
|
|||||||
LEDC_TIMER(group, timer).conf.clock_divider = div_num;
|
LEDC_TIMER(group, timer).conf.clock_divider = div_num;
|
||||||
LEDC_MUTEX_UNLOCK();
|
LEDC_MUTEX_UNLOCK();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
log_d("using REF_CLK chan=%d",chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iarg = iarg >> 1;
|
||||||
|
chan++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,11 +93,14 @@ static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, boo
|
|||||||
{
|
{
|
||||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||||
static bool tHasStarted = false;
|
static bool tHasStarted = false;
|
||||||
|
static uint16_t _activeChannels = 0;
|
||||||
if(!tHasStarted) {
|
if(!tHasStarted) {
|
||||||
tHasStarted = true;
|
tHasStarted = true;
|
||||||
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_LEDC_CLK_EN);
|
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_LEDC_CLK_EN);
|
||||||
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_LEDC_RST);
|
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_LEDC_RST);
|
||||||
LEDC.conf.apb_clk_sel = 1;//LS use apb clock
|
LEDC.conf.apb_clk_sel = 1;//LS use apb clock
|
||||||
|
addApbChangeCallback((void*)&_activeChannels, _on_apb_change);
|
||||||
|
|
||||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||||
_ledc_sys_lock = xSemaphoreCreateMutex();
|
_ledc_sys_lock = xSemaphoreCreateMutex();
|
||||||
#endif
|
#endif
|
||||||
@ -105,8 +116,7 @@ static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, boo
|
|||||||
LEDC_TIMER(group, timer).conf.rst = 1;//This bit is used to reset timer the counter will be 0 after reset.
|
LEDC_TIMER(group, timer).conf.rst = 1;//This bit is used to reset timer the counter will be 0 after reset.
|
||||||
LEDC_TIMER(group, timer).conf.rst = 0;
|
LEDC_TIMER(group, timer).conf.rst = 0;
|
||||||
LEDC_MUTEX_UNLOCK();
|
LEDC_MUTEX_UNLOCK();
|
||||||
uint32_t iarg = chan;
|
_activeChannels |= (1 << chan); // mark as active for APB callback
|
||||||
addApbChangeCallback((void*)iarg, _on_apb_change);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//max div_num 0x3FFFF (262143)
|
//max div_num 0x3FFFF (262143)
|
||||||
|
@ -217,7 +217,6 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx
|
|||||||
if(txPin != -1) {
|
if(txPin != -1) {
|
||||||
uartAttachTx(uart, txPin, inverted);
|
uartAttachTx(uart, txPin, inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
addApbChangeCallback(uart, uart_on_apb_change);
|
addApbChangeCallback(uart, uart_on_apb_change);
|
||||||
return uart;
|
return uart;
|
||||||
}
|
}
|
||||||
@ -377,18 +376,21 @@ static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old
|
|||||||
uart->dev->int_clr.val = 0xffffffff;
|
uart->dev->int_clr.val = 0xffffffff;
|
||||||
// read RX fifo
|
// read RX fifo
|
||||||
uint8_t c;
|
uint8_t c;
|
||||||
BaseType_t xHigherPriorityTaskWoken;
|
// BaseType_t xHigherPriorityTaskWoken;
|
||||||
while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
|
while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
|
||||||
c = uart->dev->fifo.rw_byte;
|
c = uart->dev->fifo.rw_byte;
|
||||||
if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
|
if(uart->queue != NULL ) {
|
||||||
xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
|
xQueueSend(uart->queue, &c, 1); //&xHigherPriorityTaskWoken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
|
||||||
// wait TX empty
|
// wait TX empty
|
||||||
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
|
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
|
||||||
} else {
|
} else {
|
||||||
//todo:
|
//todo:
|
||||||
// set baudrate
|
// set baudrate
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
|
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
|
||||||
uint32_t baud_rate = ((old_apb<<4)/clk_div);
|
uint32_t baud_rate = ((old_apb<<4)/clk_div);
|
||||||
clk_div = ((new_apb<<4)/baud_rate);
|
clk_div = ((new_apb<<4)/baud_rate);
|
||||||
|
Loading…
Reference in New Issue
Block a user