// Copyright 2016-2018 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "sdkconfig.h" #include "esp_types.h" #include "esp_log.h" #include "soc/rtc_periph.h" #include "soc/syscon_periph.h" #include "soc/rtc.h" #include "soc/periph_defs.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/timers.h" #include "esp_intr_alloc.h" #include "sys/lock.h" #include "driver/rtc_cntl.h" #ifndef NDEBUG // Enable built-in checks in queue.h in debug builds #define INVARIANTS #endif #include "sys/queue.h" portMUX_TYPE rtc_spinlock = portMUX_INITIALIZER_UNLOCKED; /*--------------------------------------------------------------- INTERRUPT HANDLER ---------------------------------------------------------------*/ typedef struct rtc_isr_handler_ { uint32_t mask; intr_handler_t handler; void* handler_arg; SLIST_ENTRY(rtc_isr_handler_) next; } rtc_isr_handler_t; static SLIST_HEAD(rtc_isr_handler_list_, rtc_isr_handler_) s_rtc_isr_handler_list = SLIST_HEAD_INITIALIZER(s_rtc_isr_handler_list); portMUX_TYPE s_rtc_isr_handler_list_lock = portMUX_INITIALIZER_UNLOCKED; static intr_handle_t s_rtc_isr_handle; static void rtc_isr(void* arg) { uint32_t status = REG_READ(RTC_CNTL_INT_ST_REG); rtc_isr_handler_t* it; portENTER_CRITICAL_ISR(&s_rtc_isr_handler_list_lock); SLIST_FOREACH(it, &s_rtc_isr_handler_list, next) { if (it->mask & status) { portEXIT_CRITICAL_ISR(&s_rtc_isr_handler_list_lock); (*it->handler)(it->handler_arg); portENTER_CRITICAL_ISR(&s_rtc_isr_handler_list_lock); } } portEXIT_CRITICAL_ISR(&s_rtc_isr_handler_list_lock); REG_WRITE(RTC_CNTL_INT_CLR_REG, status); } static esp_err_t rtc_isr_ensure_installed(void) { esp_err_t err = ESP_OK; portENTER_CRITICAL(&s_rtc_isr_handler_list_lock); if (s_rtc_isr_handle) { goto out; } REG_WRITE(RTC_CNTL_INT_ENA_REG, 0); REG_WRITE(RTC_CNTL_INT_CLR_REG, UINT32_MAX); err = esp_intr_alloc(ETS_RTC_CORE_INTR_SOURCE, 0, &rtc_isr, NULL, &s_rtc_isr_handle); if (err != ESP_OK) { goto out; } out: portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); return err; } esp_err_t rtc_isr_register(intr_handler_t handler, void* handler_arg, uint32_t rtc_intr_mask) { esp_err_t err = rtc_isr_ensure_installed(); if (err != ESP_OK) { return err; } rtc_isr_handler_t* item = malloc(sizeof(*item)); if (item == NULL) { return ESP_ERR_NO_MEM; } item->handler = handler; item->handler_arg = handler_arg; item->mask = rtc_intr_mask; portENTER_CRITICAL(&s_rtc_isr_handler_list_lock); SLIST_INSERT_HEAD(&s_rtc_isr_handler_list, item, next); portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); return ESP_OK; } esp_err_t rtc_isr_deregister(intr_handler_t handler, void* handler_arg) { rtc_isr_handler_t* it; rtc_isr_handler_t* prev = NULL; bool found = false; portENTER_CRITICAL(&s_rtc_isr_handler_list_lock); SLIST_FOREACH(it, &s_rtc_isr_handler_list, next) { if (it->handler == handler && it->handler_arg == handler_arg) { if (it == SLIST_FIRST(&s_rtc_isr_handler_list)) { SLIST_REMOVE_HEAD(&s_rtc_isr_handler_list, next); } else { SLIST_REMOVE_AFTER(prev, next); } found = true; free(it); break; } prev = it; } portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); return found ? ESP_OK : ESP_ERR_INVALID_STATE; }