From cbe23147bfbbdc0d7d26af150c2b04bdcb9b682a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 2 Jun 2017 17:47:23 +0800 Subject: [PATCH] driver/rtc: add support for attaching handlers to RTC_CNTL interrupts --- components/driver/include/driver/rtc_cntl.h | 48 +++++++++ components/driver/rtc_module.c | 106 ++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 components/driver/include/driver/rtc_cntl.h diff --git a/components/driver/include/driver/rtc_cntl.h b/components/driver/include/driver/rtc_cntl.h new file mode 100644 index 0000000000..a79745713e --- /dev/null +++ b/components/driver/include/driver/rtc_cntl.h @@ -0,0 +1,48 @@ +// Copyright 2016-2017 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. + +#pragma once + +#include +#include "esp_err.h" +#include "esp_intr_alloc.h" + +/** + * @brief Register a handler for specific RTC_CNTL interrupts + * + * Multiple handlers can be registered using this function. Whenever an + * RTC interrupt happens, all handlers with matching rtc_intr_mask values + * will be called. + * + * @param handler handler function to call + * @param handler_arg argument to be passed to the handler + * @param rtc_intr_mask combination of RTC_CNTL_*_INT_ENA bits indicating the + * sources to call the handler for + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM not enough memory to allocate handler structure + * - other errors returned by esp_intr_alloc + */ +esp_err_t rtc_isr_register(intr_handler_t handler, void* handler_arg, + uint32_t rtc_intr_mask); +/** + * @brief Deregister the handler previously registered using rtc_isr_register + * @param handler handler function to call (as passed to rtc_isr_register) + * @param handler_arg argument of the handler (as passed to rtc_isr_register) + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if a handler matching both handler and + * handler_arg isn't registered + */ +esp_err_t rtc_isr_deregister(intr_handler_t handler, void* handler_arg); diff --git a/components/driver/rtc_module.c b/components/driver/rtc_module.c index 8de8ee2497..c6d2727e02 100644 --- a/components/driver/rtc_module.c +++ b/components/driver/rtc_module.c @@ -24,6 +24,16 @@ #include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" #include "freertos/semphr.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 "rom/queue.h" + static const char *RTC_MODULE_TAG = "RTC_MODULE"; @@ -792,3 +802,99 @@ int hall_sensor_read() adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_0db); return hall_sensor_get_value(); } + +/*--------------------------------------------------------------- + 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(&s_rtc_isr_handler_list_lock); + SLIST_FOREACH(it, &s_rtc_isr_handler_list, next) { + if (it->mask & status) { + portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); + (*it->handler)(it->handler_arg); + portENTER_CRITICAL(&s_rtc_isr_handler_list_lock); + } + } + portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); + REG_WRITE(RTC_CNTL_INT_CLR_REG, status); +} + +static esp_err_t rtc_isr_ensure_installed() +{ + 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; + break; + } + prev = it; + } + portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); + return found ? ESP_OK : ESP_ERR_INVALID_STATE; +} -- 2.40.0