/* Board */ #include "board.h" #include "clock_config.h" #include "pin_mux.h" /* FreeRTOS */ /* clang-format off */ #include "FreeRTOS.h" #include "task.h" #include "semphr.h" /* clang-format on */ /* SDK drivers */ #include "fsl_gpio.h" #include "fsl_pint.h" #include "fsl_spi.h" /* Debug Console */ #include "fsl_debug_console.h" /* Private */ #include "app_nh_impl.h" static int app_nh_impl_spi_init(void); static int app_nh_impl_pin_init(void); static inline bool app_nh_impl_is_isr(void); static void app_nh_impl_drdy_callback(pint_pin_int_t pintr, uint32_t pmatch_status); static void app_nh_impl_hs_callback(pint_pin_int_t pintr, uint32_t pmatch_status); static nh_ret_t app_nh_impl_ops_xfer(void *handle, uint8_t *tx_data, uint8_t *rx_data, uint32_t len); static nh_ret_t app_nh_impl_ops_drdy_read(void *handle, bool *rdy); static nh_ret_t app_nh_impl_ops_hs_poll(void *handle, uint32_t timeout_ms); static void app_nh_impl_cb_startup(void *handle, nh_event_init_t *init_data); static void app_nh_impl_cb_init(void *handle); static nh_ret_t app_nh_impl_buf_allocate(void *handle, uint8_t **buf, uint32_t size); static nh_ret_t app_nh_impl_buf_free(void *handle, uint8_t *buf); static nh_ret_t app_nh_impl_semaphore_create(void *handle, nh_osa_semaphore_t *sem); static nh_ret_t app_nh_impl_semaphore_take(void *handle, nh_osa_semaphore_t sem, uint32_t timeout_msec); static nh_ret_t app_nh_impl_semaphore_give(void *handle, nh_osa_semaphore_t sem); static nh_ret_t app_nh_impl_semaphore_destroy(void *handle, nh_osa_semaphore_t sem); static nh_ret_t app_nh_impl_queue_create(void *handle, nh_osa_queue_t *queue, uint32_t item_size, uint32_t max_length); static nh_ret_t app_nh_impl_queue_enqueue(void *handle, nh_osa_queue_t queue, void *item, uint32_t timeout_msec); static nh_ret_t app_nh_impl_queue_dequeue(void *handle, nh_osa_queue_t queue, void *item, uint32_t timeout_msec); static nh_ret_t app_impl_queue_destroy(void *handle, nh_osa_queue_t queue); static void app_nh_impl_shared_if_task(void *parameters); static void app_nh_impl_ctrl_task(void *parameters); static SemaphoreHandle_t s_esp_init_semphore; static nh_osa_t s_nh_osa = { .buf_allocate = app_nh_impl_buf_allocate, .buf_free = app_nh_impl_buf_free, .sem_create = app_nh_impl_semaphore_create, .sem_give = app_nh_impl_semaphore_give, .sem_take = app_nh_impl_semaphore_take, .sem_destroy = app_nh_impl_semaphore_destroy, .queue_create = app_nh_impl_queue_create, .queue_enqueue = app_nh_impl_queue_enqueue, .queue_dequeue = app_nh_impl_queue_dequeue, .queue_destroy = app_impl_queue_destroy, }; nh_shared_if_t g_nh_shared_if = { .osa = &s_nh_osa, .ops = { .xfer = app_nh_impl_ops_xfer, .drdy_read = app_nh_impl_ops_drdy_read, .hs_poll = app_nh_impl_ops_hs_poll, }, .cb = { .init = app_nh_impl_cb_startup, }, }; nh_ctrl_api_t g_nh_ctrl_api = { .osa = &s_nh_osa, .shared_if = &g_nh_shared_if, .cb = { .init = app_nh_impl_cb_init, }, }; int app_nh_impl_init(void) { s_esp_init_semphore = xSemaphoreCreateBinary(); if (s_esp_init_semphore == NULL) { return -1; } if (app_nh_impl_pin_init() != 0) { return -1; } if (app_nh_impl_spi_init() != 0) { return -2; } if (nh_shared_if_init(&g_nh_shared_if) != NH_RET_SUCCESS) { return -3; } if (xTaskCreate(app_nh_impl_shared_if_task, "NH_IF", 512, &g_nh_shared_if, 2, NULL) != pdPASS) { return -4; } if (nh_ctrl_api_init(&g_nh_ctrl_api) != NH_RET_SUCCESS) { return -5; } if (xTaskCreate(app_nh_impl_ctrl_task, "NH_CTRL", 512, &g_nh_ctrl_api, 2, NULL) != pdPASS) { return -6; } /* FIXME: De-init properly */ if (xSemaphoreTake(s_esp_init_semphore, pdMS_TO_TICKS(5000)) != pdPASS) { PRINTF("Did not received ESPInit event in time, bailing out (assumed soft reset).\r\n"); } return 0; } static int app_nh_impl_spi_init(void) { spi_master_config_t cfg; /* Main Clock to SPI */ CLOCK_AttachClk(kMAIN_CLK_to_HSLSPI); SPI_MasterGetDefaultConfig(&cfg); cfg.baudRate_Bps = 20000000UL; cfg.sselNum = kSPI_Ssel1; cfg.phase = kSPI_ClockPhaseSecondEdge; /* SPI Mode 1 */ if (SPI_MasterInit(SPI8, &cfg, CLOCK_GetHsLspiClkFreq()) != kStatus_Success) { return -1; } return 0; } static int app_nh_impl_pin_init(void) { /* Configure pin interrupt */ PINT_Init(PINT); PINT_PinInterruptConfig(PINT, kPINT_PinInt0, kPINT_PinIntEnableRiseEdge, app_nh_impl_hs_callback); PINT_EnableCallbackByIndex(PINT, kPINT_PinInt0); PINT_PinInterruptConfig(PINT, kPINT_PinInt1, kPINT_PinIntEnableRiseEdge, app_nh_impl_drdy_callback); PINT_EnableCallbackByIndex(PINT, kPINT_PinInt1); EnableIRQ(PIN_INT0_IRQn); NVIC_SetPriority(PIN_INT0_IRQn, 5); EnableIRQ(PIN_INT1_IRQn); NVIC_SetPriority(PIN_INT1_IRQn, 5); return 0; } static inline bool app_nh_impl_is_isr(void) { if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) { /* We are in ISR... */ return true; } return false; } static void app_nh_impl_drdy_callback(pint_pin_int_t pintr, uint32_t pmatch_status) { /* If there's a packet to be received by host, the DRDY is set high. */ if (GPIO_PinRead(GPIO, BOARD_INITMIKROEPINS_ESP_HS_PORT, BOARD_INITMIKROEPINS_ESP_HS_PIN)) { nh_shared_if_inject_data_ready(&g_nh_shared_if); } } static void app_nh_impl_hs_callback(pint_pin_int_t pintr, uint32_t pmatch_status) { /* If the DRDY is high when HS rises, there's another packet waiting... */ if (GPIO_PinRead(GPIO, BOARD_INITMIKROEPINS_ESP_DRDY_PORT, BOARD_INITMIKROEPINS_ESP_DRDY_PIN)) { nh_shared_if_inject_data_ready(&g_nh_shared_if); } } static nh_ret_t app_nh_impl_ops_drdy_read(void *handle, bool *rdy) { if (GPIO_PinRead(GPIO, BOARD_INITMIKROEPINS_ESP_DRDY_PORT, BOARD_INITMIKROEPINS_ESP_DRDY_PIN)) { *rdy = true; } else { *rdy = false; } return NH_RET_SUCCESS; } static nh_ret_t app_nh_impl_ops_hs_poll(void *handle, uint32_t timeout_ms) { TickType_t tick_start = xTaskGetTickCount(); while (GPIO_PinRead(GPIO, BOARD_INITMIKROEPINS_ESP_HS_PORT, BOARD_INITMIKROEPINS_ESP_HS_PIN) == 0) { vTaskDelay(pdMS_TO_TICKS(20)); if (xTaskGetTickCount() - tick_start > pdMS_TO_TICKS(timeout_ms)) { return NH_RET_TIMEOUT; } } return NH_RET_SUCCESS; } static nh_ret_t app_nh_impl_ops_xfer(void *handle, uint8_t *tx_data, uint8_t *rx_data, uint32_t len) { spi_transfer_t xfer = { .txData = tx_data, .rxData = rx_data, .dataSize = len, .configFlags = kSPI_FrameAssert, }; if (SPI_MasterTransferBlocking(SPI8, &xfer) != kStatus_Success) { return NH_RET_FAIL; } return NH_RET_SUCCESS; } static void app_nh_impl_cb_startup(void *handle, nh_event_init_t *init_data) { PRINTF("Received init data and device capabilities from ESP:\r\n"); char *buf; switch (init_data->chip_id) { case NH_ESP_INIT_CHIP_ESP32: buf = "ESP32"; break; case NH_ESP_INIT_CHIP_ESP32S2: buf = "ESP32S2"; break; case NH_ESP_INIT_CHIP_ESP32S3: buf = "ESP32S3"; break; case NH_ESP_INIT_CHIP_ESP32C2: buf = "ESP32C2"; break; case NH_ESP_INIT_CHIP_ESP32C3: buf = "ESP32C3"; break; default: buf = "UNKNOWN"; break; } PRINTF("Target SoC is: %s\r\n", buf); PRINTF("Maximum SPI frequency: %dMHz\r\n", init_data->spi_freq); PRINTF("Target capabilities: "); if (init_data->capabilities & NH_EVENT_INIT_CAP_WLAN_SPI) { PRINTF("WLAN_SPI | "); } if (init_data->capabilities & NH_EVENT_INIT_CAP_BT_SPI) { PRINTF("BT_SPI | "); } if (init_data->capabilities & NH_EVENT_INIT_CAP_WLAN_SDIO) { PRINTF("WLAN_SDIO | "); } if (init_data->capabilities & NH_EVENT_INIT_CAP_BT_SDIO) { PRINTF("BT_SDIO | "); } if (init_data->capabilities & NH_EVENT_INIT_CAP_BT_UART) { PRINTF("BT_UART | "); } if (init_data->capabilities & NH_EVENT_INIT_CAP_BLE_ONLY) { PRINTF("BLE_ONLY | "); } if (init_data->capabilities & NH_EVENT_INIT_CAP_BR_EDR_ONLY) { PRINTF("BR_EDR_ONLY | "); } if (init_data->capabilities & NH_EVENT_INIT_CAP_CHECKSUM) { PRINTF("CHECKSUM | "); } PRINTF("\r\n"); } static void app_nh_impl_cb_init(void *handle) { PRINTF("ESPStart event received.\r\n"); xSemaphoreGive(s_esp_init_semphore); } static nh_ret_t app_nh_impl_buf_allocate(void *handle, uint8_t **buf, uint32_t size) { *buf = pvPortMalloc(size); if (*buf == NULL) { return NH_RET_FAIL; } return NH_RET_SUCCESS; } static nh_ret_t app_nh_impl_buf_free(void *handle, uint8_t *buf) { vPortFree(buf); return NH_RET_SUCCESS; } static nh_ret_t app_nh_impl_semaphore_create(void *handle, nh_osa_semaphore_t *sem) { *sem = xSemaphoreCreateBinary(); if (*sem == NULL) { return NH_RET_FAIL; } return NH_RET_SUCCESS; } static nh_ret_t app_nh_impl_semaphore_take(void *handle, nh_osa_semaphore_t sem, uint32_t timeout_msec) { nh_ret_t ret = NH_RET_SUCCESS; if (app_nh_impl_is_isr()) { if (timeout_msec != 0) { /* Block in ISR is not permitted */ return NH_RET_FAIL; } BaseType_t higher_prio_task_woken = pdFALSE; if (xSemaphoreTakeFromISR(sem, &higher_prio_task_woken) != pdPASS) { ret = NH_RET_TIMEOUT; } portYIELD_FROM_ISR(higher_prio_task_woken); } else { if (xSemaphoreTake(sem, pdMS_TO_TICKS(timeout_msec)) != pdPASS) { ret = NH_RET_TIMEOUT; } } return ret; } static nh_ret_t app_nh_impl_semaphore_give(void *handle, nh_osa_semaphore_t sem) { nh_ret_t ret = NH_RET_SUCCESS; if (app_nh_impl_is_isr()) { BaseType_t higher_prio_task_woken = pdFALSE; if (xSemaphoreGiveFromISR(sem, &higher_prio_task_woken) != pdPASS) { ret = NH_RET_FAIL; } portYIELD_FROM_ISR(higher_prio_task_woken); } else { if (xSemaphoreGive(sem) != pdPASS) { ret = NH_RET_FAIL; } } return ret; } static nh_ret_t app_nh_impl_semaphore_destroy(void *handle, nh_osa_semaphore_t sem) { vSemaphoreDelete(sem); return NH_RET_SUCCESS; } static nh_ret_t app_nh_impl_queue_create(void *handle, nh_osa_queue_t *queue, uint32_t item_size, uint32_t max_length) { *queue = xQueueCreate(max_length, item_size); if (*queue == NULL) { return NH_RET_FAIL; } return NH_RET_SUCCESS; } static nh_ret_t app_nh_impl_queue_enqueue(void *handle, nh_osa_queue_t queue, void *item, uint32_t timeout_msec) { nh_ret_t ret = NH_RET_SUCCESS; if (app_nh_impl_is_isr()) { if (timeout_msec != 0) { return NH_RET_FAIL; } BaseType_t higher_prio_task_woken = pdFALSE; if (xQueueSendFromISR(queue, item, &higher_prio_task_woken) != pdPASS) { ret = NH_RET_TIMEOUT; } portYIELD_FROM_ISR(higher_prio_task_woken); } else { if (xQueueSend(queue, item, pdMS_TO_TICKS(timeout_msec)) != pdPASS) { ret = NH_RET_TIMEOUT; } } return ret; } static nh_ret_t app_nh_impl_queue_dequeue(void *handle, nh_osa_queue_t queue, void *item, uint32_t timeout_msec) { nh_ret_t ret = NH_RET_SUCCESS; if (app_nh_impl_is_isr()) { if (timeout_msec != 0) { return NH_RET_FAIL; } BaseType_t higher_prio_task_woken = pdFALSE; if (xQueueReceiveFromISR(queue, item, &higher_prio_task_woken) != pdPASS) { ret = NH_RET_TIMEOUT; } portYIELD_FROM_ISR(higher_prio_task_woken); } else { if (xQueueReceive(queue, item, pdMS_TO_TICKS(timeout_msec)) != pdPASS) { ret = NH_RET_TIMEOUT; } } return ret; } static nh_ret_t app_impl_queue_destroy(void *handle, nh_osa_queue_t queue) { vQueueDelete(queue); return NH_RET_SUCCESS; } static void app_nh_impl_shared_if_task(void *parameters) { for (;;) { nh_shared_if_task(&g_nh_shared_if); } vTaskDelete(NULL); } static void app_nh_impl_ctrl_task(void *parameters) { for (;;) { nh_ctrl_api_task(&g_nh_ctrl_api); } vTaskDelete(NULL); }