diff --git a/components/services/gpio_exp.c b/components/services/gpio_exp.c index 1740d3a4..fa381f91 100644 --- a/components/services/gpio_exp.c +++ b/components/services/gpio_exp.c @@ -29,6 +29,8 @@ typedef struct gpio_exp_s { uint32_t first, last; + int intr; + bool intr_pending; struct { struct gpio_exp_phy_s phy; spi_device_handle_t spi_handle; @@ -176,6 +178,7 @@ gpio_exp_t* gpio_exp_create(const gpio_exp_config_t *config) { n_expanders++; expander->first = config->base; expander->last = config->base + config->count - 1; + expander->intr = config->intr; expander->mutex = xSemaphoreCreateMutex(); // create a task to handle asynchronous requests (only write at this time) @@ -189,7 +192,7 @@ gpio_exp_t* gpio_exp_create(const gpio_exp_config_t *config) { } // set interrupt if possible - if (config->intr > 0) { + if (config->intr >= 0) { gpio_pad_select_gpio(config->intr); gpio_set_direction(config->intr, GPIO_MODE_INPUT); @@ -402,12 +405,16 @@ esp_err_t gpio_isr_handler_remove_x(int gpio) { * INTR low-level handler */ static void IRAM_ATTR intr_isr_handler(void* arg) { + gpio_exp_t *self = (gpio_exp_t*) arg; BaseType_t woken = pdFALSE; + // activate all, including ourselves + for (int i = 0; i < n_expanders; i++) if (expanders[i].intr == self->intr) expanders[i].intr_pending = true; + xTaskNotifyFromISR(service_task, GPIO_EXP_INTR, eSetValueWithOverwrite, &woken); if (woken) portYIELD_FROM_ISR(); - ESP_EARLY_LOGD(TAG, "INTR for expander base %d", gpio_exp_get_base(arg)); + ESP_EARLY_LOGD(TAG, "INTR for expander base %d", gpio_exp_get_base(self)); } /**************************************************************************************** @@ -433,6 +440,18 @@ void service_handler(void *arg) { now, a loop will do */ for (int i = 0; i < n_expanders; i++) { gpio_exp_t *expander = expanders + i; + + // no interrupt for that gpio + if (expander->intr < 0) continue; + + // only check expander with pending interrupts + gpio_intr_disable(expander->intr); + if (!expander->intr_pending) { + gpio_intr_enable(expander->intr); + continue; + } + expander->intr_pending = false; + gpio_intr_enable(expander->intr); xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(50)); diff --git a/components/services/gpio_exp.h b/components/services/gpio_exp.h index 1ab47197..91339880 100644 --- a/components/services/gpio_exp.h +++ b/components/services/gpio_exp.h @@ -16,7 +16,7 @@ struct gpio_exp_s; typedef struct { char model[32]; - uint8_t intr; + int intr; uint8_t count; uint32_t base; struct gpio_exp_phy_s { diff --git a/components/squeezelite/decode_external.c b/components/squeezelite/decode_external.c index 0f6bfafd..296fde01 100644 --- a/components/squeezelite/decode_external.c +++ b/components/squeezelite/decode_external.c @@ -37,6 +37,8 @@ static EXT_RAM_ATTR struct { } raop_sync; #endif +static bool abort_sink ; + #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #define LOCK_D mutex_lock(decode.mutex); @@ -63,11 +65,12 @@ static void sink_data_handler(const uint8_t *data, uint32_t len) LOG_SDEBUG("Cannot use external sink while LMS is controlling player"); return; } - - // there will always be room at some point - while (len) { - LOCK_O; + LOCK_O; + abort_sink = false; + + // there will always be room at some point + while (len && wait && !abort_sink) { bytes = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / (BYTES_PER_FRAME / 4); bytes = min(len, bytes); #if BYTES_PER_FRAME == 4 @@ -86,11 +89,16 @@ static void sink_data_handler(const uint8_t *data, uint32_t len) len -= bytes; data += bytes; - UNLOCK_O; - // allow i2s to empty the buffer if needed - if (len && !space && wait--) usleep(20000); + if (len && !space) { + wait--; + UNLOCK_O; + usleep(50000); + LOCK_O; + } } + + UNLOCK_O; if (!wait) { LOG_WARN("Waited too long, dropping frames"); @@ -105,7 +113,7 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) { // don't LOCK_O as there is always a chance that LMS takes control later anyway if (output.external != DECODE_BT && output.state > OUTPUT_STOPPED) { - LOG_WARN("Cannot use BT sink while LMS/AirPlay is controlling player"); + LOG_WARN("Cannot use BT sink while LMS/AirPlay are controlling player"); return false; } @@ -115,11 +123,11 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) switch(cmd) { case BT_SINK_AUDIO_STARTED: + _buf_flush(outputbuf); output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t); output.external = DECODE_BT; output.state = OUTPUT_STOPPED; output.frames_played = 0; - _buf_flush(outputbuf); if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR; LOG_INFO("BT sink started"); break; @@ -132,17 +140,18 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) break; case BT_SINK_PLAY: output.state = OUTPUT_RUNNING; - LOG_INFO("BT playing"); + LOG_INFO("BT play"); break; case BT_SINK_STOP: _buf_flush(outputbuf); output.state = OUTPUT_STOPPED; output.stop_time = gettime_ms(); - LOG_INFO("BT stopped"); + abort_sink = true; + LOG_INFO("BT stop"); break; case BT_SINK_PAUSE: output.stop_time = gettime_ms(); - LOG_INFO("BT paused, just silence"); + LOG_INFO("BT pause, just silence"); break; case BT_SINK_RATE: output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t); @@ -184,7 +193,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) { // don't LOCK_O as there is always a chance that LMS takes control later anyway if (output.external != DECODE_RAOP && output.state > OUTPUT_STOPPED) { - LOG_WARN("Cannot use Airplay sink while LMS/BT is controlling player"); + LOG_WARN("Cannot use Airplay sink while LMS/BT are controlling player"); return false; } @@ -269,6 +278,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) raop_state = event; _buf_flush(outputbuf); if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED; + abort_sink = true; output.frames_played = 0; output.stop_time = gettime_ms(); break;