/ components / esp_ipc / ipc.c
ipc.c
  1  // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2  //
  3  // Licensed under the Apache License, Version 2.0 (the "License");
  4  // you may not use this file except in compliance with the License.
  5  // You may obtain a copy of the License at
  6  
  7  //     http://www.apache.org/licenses/LICENSE-2.0
  8  //
  9  // Unless required by applicable law or agreed to in writing, software
 10  // distributed under the License is distributed on an "AS IS" BASIS,
 11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  // See the License for the specific language governing permissions and
 13  // limitations under the License.
 14  
 15  #include <stddef.h>
 16  #include <stdlib.h>
 17  #include <string.h>
 18  #include <assert.h>
 19  #include "esp_err.h"
 20  #include "esp_ipc.h"
 21  #include "esp_attr.h"
 22  
 23  #include "freertos/FreeRTOS.h"
 24  #include "freertos/task.h"
 25  #include "freertos/semphr.h"
 26  
 27  static TaskHandle_t s_ipc_task_handle[portNUM_PROCESSORS];
 28  static SemaphoreHandle_t s_ipc_mutex[portNUM_PROCESSORS];    // This mutex is used as a global lock for esp_ipc_* APIs
 29  static SemaphoreHandle_t s_ipc_sem[portNUM_PROCESSORS];      // Two semaphores used to wake each of ipc tasks
 30  static SemaphoreHandle_t s_ipc_ack[portNUM_PROCESSORS];      // Semaphore used to acknowledge that task was woken up,
 31                                                               //   or function has finished running
 32  static volatile esp_ipc_func_t s_func[portNUM_PROCESSORS];   // Function which should be called by high priority task
 33  static void * volatile s_func_arg[portNUM_PROCESSORS];       // Argument to pass into s_func
 34  typedef enum {
 35      IPC_WAIT_FOR_START,
 36      IPC_WAIT_FOR_END
 37  } esp_ipc_wait_t;
 38  
 39  static volatile esp_ipc_wait_t s_ipc_wait[portNUM_PROCESSORS];// This variable tells high priority task when it should give
 40                                                               //   s_ipc_ack semaphore: before s_func is called, or
 41                                                               //   after it returns
 42  
 43  static void IRAM_ATTR ipc_task(void* arg)
 44  {
 45      const uint32_t cpuid = (uint32_t) arg;
 46      assert(cpuid == xPortGetCoreID());
 47      while (true) {
 48          // Wait for IPC to be initiated.
 49          // This will be indicated by giving the semaphore corresponding to
 50          // this CPU.
 51          if (xSemaphoreTake(s_ipc_sem[cpuid], portMAX_DELAY) != pdTRUE) {
 52              // TODO: when can this happen?
 53              abort();
 54          }
 55  
 56          esp_ipc_func_t func = s_func[cpuid];
 57          void* arg = s_func_arg[cpuid];
 58  
 59          if (s_ipc_wait[cpuid] == IPC_WAIT_FOR_START) {
 60              xSemaphoreGive(s_ipc_ack[cpuid]);
 61          }
 62          (*func)(arg);
 63          if (s_ipc_wait[cpuid] == IPC_WAIT_FOR_END) {
 64              xSemaphoreGive(s_ipc_ack[cpuid]);
 65          }
 66      }
 67      // TODO: currently this is unreachable code. Introduce esp_ipc_uninit
 68      // function which will signal to both tasks that they can shut down.
 69      // Not critical at this point, we don't have a use case for stopping
 70      // IPC yet.
 71      // Also need to delete the semaphore here.
 72      vTaskDelete(NULL);
 73  }
 74  
 75  /*
 76   * Initialize inter-processor call module. This function is called automatically
 77   * on CPU start and should not be called from the application.
 78   *
 79   * This function start two tasks, one on each CPU. These tasks are started
 80   * with high priority. These tasks are normally inactive, waiting until one of
 81   * the esp_ipc_call_* functions to be used. One of these tasks will be
 82   * woken up to execute the callback provided to esp_ipc_call_nonblocking or
 83   * esp_ipc_call_blocking.
 84   */
 85  static void esp_ipc_init(void) __attribute__((constructor));
 86  
 87  static void esp_ipc_init(void)
 88  {
 89      char task_name[15];
 90      for (int i = 0; i < portNUM_PROCESSORS; ++i) {
 91          snprintf(task_name, sizeof(task_name), "ipc%d", i);
 92          s_ipc_mutex[i] = xSemaphoreCreateMutex();
 93          s_ipc_ack[i] = xSemaphoreCreateBinary();
 94          s_ipc_sem[i] = xSemaphoreCreateBinary();
 95          portBASE_TYPE res = xTaskCreatePinnedToCore(ipc_task, task_name, CONFIG_ESP_IPC_TASK_STACK_SIZE, (void*) i,
 96                                                      configMAX_PRIORITIES - 1, &s_ipc_task_handle[i], i);
 97          assert(res == pdTRUE);
 98      }
 99  }
100  
101  static esp_err_t esp_ipc_call_and_wait(uint32_t cpu_id, esp_ipc_func_t func, void* arg, esp_ipc_wait_t wait_for)
102  {
103      if (cpu_id >= portNUM_PROCESSORS) {
104          return ESP_ERR_INVALID_ARG;
105      }
106      if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
107          return ESP_ERR_INVALID_STATE;
108      }
109  
110  #ifdef CONFIG_ESP_IPC_USES_CALLERS_PRIORITY
111      TaskHandle_t task_handler = xTaskGetCurrentTaskHandle();
112      UBaseType_t priority_of_current_task = uxTaskPriorityGet(task_handler);
113      UBaseType_t priority_of_running_ipc_task = uxTaskPriorityGet(s_ipc_task_handle[cpu_id]);
114      if (priority_of_running_ipc_task < priority_of_current_task) {
115          vTaskPrioritySet(s_ipc_task_handle[cpu_id], priority_of_current_task);
116      }
117  
118      xSemaphoreTake(s_ipc_mutex[cpu_id], portMAX_DELAY);
119      vTaskPrioritySet(s_ipc_task_handle[cpu_id], priority_of_current_task);
120  #else
121      xSemaphoreTake(s_ipc_mutex[0], portMAX_DELAY);
122  #endif
123  
124      s_func[cpu_id] = func;
125      s_func_arg[cpu_id] = arg;
126      s_ipc_wait[cpu_id] = wait_for;
127      xSemaphoreGive(s_ipc_sem[cpu_id]);
128      xSemaphoreTake(s_ipc_ack[cpu_id], portMAX_DELAY);
129  #ifdef CONFIG_ESP_IPC_USES_CALLERS_PRIORITY
130      xSemaphoreGive(s_ipc_mutex[cpu_id]);
131  #else
132      xSemaphoreGive(s_ipc_mutex[0]);
133  #endif
134      return ESP_OK;
135  }
136  
137  esp_err_t esp_ipc_call(uint32_t cpu_id, esp_ipc_func_t func, void* arg)
138  {
139      return esp_ipc_call_and_wait(cpu_id, func, arg, IPC_WAIT_FOR_START);
140  }
141  
142  esp_err_t esp_ipc_call_blocking(uint32_t cpu_id, esp_ipc_func_t func, void* arg)
143  {
144      return esp_ipc_call_and_wait(cpu_id, func, arg, IPC_WAIT_FOR_END);
145  }
146