diff --git a/espradio/osi.c b/espradio/osi.c index 646476b..3d071a8 100644 --- a/espradio/osi.c +++ b/espradio/osi.c @@ -6,10 +6,6 @@ // Documentation for these functions: // https://github.com/esp-rs/esp-wifi/blob/main/esp-wifi/src/wifi/os_adapter.rs -// Fake task handle. Used for espradio_task_get_current_task. -// TODO: get the real task handle somehow. -static uint8_t current_task = 1; - __attribute__((noreturn)) void espradio_panic(char *s); @@ -45,13 +41,9 @@ void *espradio_spin_lock_create(void); void espradio_spin_lock_delete(void *lock); -static uint32_t espradio_wifi_int_disable(void *wifi_int_mux) { - espradio_panic("todo: _wifi_int_disable"); -} +uint32_t espradio_wifi_int_disable(void *wifi_int_mux); -static void espradio_wifi_int_restore(void *wifi_int_mux, uint32_t tmp) { - espradio_panic("todo: _wifi_int_restore"); -} +void espradio_wifi_int_restore(void *wifi_int_mux, uint32_t tmp); static void espradio_task_yield_from_isr(void) { espradio_panic("todo: _task_yield_from_isr"); @@ -59,21 +51,13 @@ static void espradio_task_yield_from_isr(void) { void *espradio_semphr_create(uint32_t max, uint32_t init); -static void espradio_semphr_delete(void *semphr) { - espradio_panic("todo: _semphr_delete"); -} +void espradio_semphr_delete(void *semphr); -static int32_t espradio_semphr_take(void *semphr, uint32_t block_time_tick) { - espradio_panic("todo: _semphr_take"); -} +int32_t espradio_semphr_take(void *semphr, uint32_t block_time_tick); -static int32_t espradio_semphr_give(void *semphr) { - espradio_panic("todo: _semphr_give"); -} +int32_t espradio_semphr_give(void *semphr); -static void *espradio_wifi_thread_semphr_get(void) { - espradio_panic("todo: _wifi_thread_semphr_get"); -} +void *espradio_wifi_thread_semphr_get(void); static void *espradio_mutex_create(void) { espradio_panic("todo: _mutex_create"); @@ -97,9 +81,7 @@ static void espradio_queue_delete(void *queue) { espradio_panic("todo: _queue_delete"); } -static int32_t espradio_queue_send(void *queue, void *item, uint32_t block_time_tick) { - espradio_panic("todo: _queue_send"); -} +int32_t espradio_queue_send(void *queue, void *item, uint32_t block_time_tick); static int32_t espradio_queue_send_from_isr(void *queue, void *item, void *hptw) { espradio_panic("todo: _queue_send_from_isr"); @@ -113,9 +95,7 @@ static int32_t espradio_queue_send_to_front(void *queue, void *item, uint32_t bl espradio_panic("todo: _queue_send_to_front"); } -static int32_t espradio_queue_recv(void *queue, void *item, uint32_t block_time_tick) { - espradio_panic("todo: _queue_recv"); -} +int32_t espradio_queue_recv(void *queue, void *item, uint32_t block_time_tick); static uint32_t espradio_queue_msg_waiting(void *queue) { espradio_panic("todo: _queue_msg_waiting"); @@ -141,30 +121,24 @@ static uint32_t espradio_event_group_wait_bits(void *event, uint32_t bits_to_wai espradio_panic("todo: _event_group_wait_bits"); } -static int32_t espradio_task_create_pinned_to_core(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle, uint32_t core_id) { - espradio_panic("todo: _task_create_pinned_to_core"); +void espradio_run_task(void *task_func, void *task_handle) { + void (*fn)(void *task_handle) = task_func; + fn(task_handle); } +int32_t espradio_task_create_pinned_to_core(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle, uint32_t core_id); + static int32_t espradio_task_create(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle) { espradio_panic("todo: _task_create"); } -static void espradio_task_delete(void *task_handle) { - espradio_panic("todo: _task_delete"); -} +void espradio_task_delete(void *task_handle); -static void espradio_task_delay(uint32_t tick) { - espradio_panic("todo: _task_delay"); -} +void espradio_task_delay(uint32_t tick); -static int32_t espradio_task_ms_to_tick(uint32_t ms) { - espradio_panic("todo: _task_ms_to_tick"); -} +int32_t espradio_task_ms_to_tick(uint32_t ms); -static void *espradio_task_get_current_task(void) { - int task_num = current_task; - return (void*)task_num; -} +void *espradio_task_get_current_task(void); static int32_t espradio_task_get_max_priority(void) { return 255; // arbitrary number @@ -373,14 +347,12 @@ static void * espradio_wifi_calloc(size_t n, size_t size) { } static void * espradio_wifi_zalloc(size_t size) { - espradio_panic("todo: _wifi_zalloc"); + return calloc(1, size); } void * espradio_wifi_create_queue(int queue_len, int item_size); -static void espradio_wifi_delete_queue(void * queue) { - espradio_panic("todo: _wifi_delete_queue"); -} +void espradio_wifi_delete_queue(void * queue); static int espradio_coex_init(void) { espradio_panic("todo: _coex_init"); diff --git a/espradio/radio.go b/espradio/radio.go index 89fe92c..f346f65 100644 --- a/espradio/radio.go +++ b/espradio/radio.go @@ -14,6 +14,7 @@ import "C" import ( "runtime/interrupt" "time" + "unsafe" ) type LogLevel uint8 @@ -60,6 +61,14 @@ func Enable(config Config) error { return nil } +func millisecondsToTicks(ms uint32) uint32 { + return ms * (ticksPerSecond / 1000) +} + +func ticksToMilliseconds(ticks uint32) uint32 { + return ticks / (ticksPerSecond / 1000) +} + //export espradio_panic func espradio_panic(msg *C.char) { panic("espradio: " + C.GoString(msg)) @@ -69,3 +78,56 @@ func espradio_panic(msg *C.char) { func espradio_log_timestamp() uint32 { return uint32(time.Now().UnixMilli()) } + +//export espradio_run_task +func espradio_run_task(task_func, param unsafe.Pointer) + +//export espradio_task_create_pinned_to_core +func espradio_task_create_pinned_to_core(task_func unsafe.Pointer, name *C.char, stack_depth uint32, param unsafe.Pointer, prio uint32, task_handle *unsafe.Pointer, core_id uint32) int32 { + ch := make(chan struct{}, 1) + go func() { + *task_handle = tinygo_task_current() + close(ch) + espradio_run_task(task_func, unsafe.Pointer(task_handle)) + }() + <-ch + return 1 +} + +//export espradio_task_delete +func espradio_task_delete(task_handle unsafe.Pointer) { + println("espradio TODO: delete task", task_handle) +} + +//export tinygo_task_current +func tinygo_task_current() unsafe.Pointer + +//export espradio_task_get_current_task +func espradio_task_get_current_task() unsafe.Pointer { + return tinygo_task_current() +} + +//export espradio_task_delay +func espradio_task_delay(ticks uint32) { + const ticksPerMillisecond = ticksPerSecond / 1000 + // Round milliseconds up. + ms := (ticks + ticksPerMillisecond - 1) / ticksPerMillisecond + time.Sleep(time.Duration(ms) * time.Millisecond) +} + +//export espradio_task_ms_to_tick +func espradio_task_ms_to_tick(ms uint32) int32 { + return int32(millisecondsToTicks(ms)) +} + +//export espradio_wifi_int_disable +func espradio_wifi_int_disable(wifi_int_mux unsafe.Pointer) uint32 { + // This is portENTER_CRITICAL (or portENTER_CRITICAL_ISR). + return uint32(interrupt.Disable()) +} + +//export espradio_wifi_int_restore +func espradio_wifi_int_restore(wifi_int_mux unsafe.Pointer, tmp uint32) { + // This is portEXIT_CRITICAL (or portEXIT_CRITICAL_ISR). + interrupt.Restore(interrupt.State(tmp)) +} diff --git a/espradio/radio_esp32c3.go b/espradio/radio_esp32c3.go index f7b74ed..44f41e4 100644 --- a/espradio/radio_esp32c3.go +++ b/espradio/radio_esp32c3.go @@ -6,7 +6,7 @@ import "device/esp" func initHardware() error { // See: - // https://github.com/esp-rs/esp-wifi/blob/main/esp-wifi/src/common_adapter/common_adapter_esp32c3.rs#L18 + // https://github.com/esp-rs/esp-wifi/blob/v0.2.0/esp-wifi/src/common_adapter/common_adapter_esp32c3.rs#L18 const ( SYSTEM_WIFIBB_RST = 1 << 0 @@ -35,3 +35,7 @@ func initHardware() error { return nil } + +// This is the value used for the ESP32-C3, see: +// https://github.com/esp-rs/esp-wifi/blob/v0.2.0/esp-wifi/src/timer/riscv.rs#L28 +const ticksPerSecond = 16_000_000 diff --git a/espradio/radio_other.go b/espradio/radio_other.go index d00f8b5..8b72ff2 100644 --- a/espradio/radio_other.go +++ b/espradio/radio_other.go @@ -4,6 +4,9 @@ package espradio import "errors" +// This is the value used for the ESP32-C3. +const ticksPerSecond = 16_000_000 + var errUnsupportedChip = errors.New("espradio: unsupported chip") func initHardware() error { diff --git a/espradio/sync.go b/espradio/sync.go index 3c205b7..a16f94e 100644 --- a/espradio/sync.go +++ b/espradio/sync.go @@ -2,9 +2,15 @@ package espradio // Various functions related to locks, mutexes, semaphores, and queues. +/* +#include "include.h" +*/ +import "C" + import ( "sync" "sync/atomic" + "time" "unsafe" ) @@ -57,25 +63,101 @@ func espradio_mutex_unlock(cmut unsafe.Pointer) int32 { return 1 } -var semaphores [1]uint32 +type semaphore chan struct{} + +var semaphores [2]semaphore var semaphoreIndex uint32 +var wifiSemaphore semaphore //export espradio_semphr_create func espradio_semphr_create(max, init uint32) unsafe.Pointer { newIndex := atomic.AddUint32(&semaphoreIndex, 1) sem := &semaphores[newIndex-1] + ch := make(semaphore, max) + for i := uint32(0); i < init; i++ { + ch <- struct{}{} + } + *sem = ch return unsafe.Pointer(sem) } +//export espradio_semphr_take +func espradio_semphr_take(semphr unsafe.Pointer, block_time_tick uint32) int32 { + sem := (*semaphore)(semphr) + if block_time_tick != C.OSI_FUNCS_TIME_BLOCKING { + panic("espradio: todo: semphr_take with timeout") + } + <-*sem + return 1 +} + +//export espradio_semphr_give +func espradio_semphr_give(semphr unsafe.Pointer) int32 { + // Note: we might need to return 0 when sending isn't possible (e.g. using a + // non-blocking send). According to the documentation of xSemaphoreGive: + // + // > pdTRUE if the semaphore was released. pdFALSE if an error occurred. + // > Semaphores are implemented using queues. An error can occur if there is + // > no space on the queue to post a message - indicating that the semaphore + // > was not first obtained correctly. + sem := (*semaphore)(semphr) + *sem <- struct{}{} + return 1 +} + +//export espradio_semphr_delete +func espradio_semphr_delete(semphr unsafe.Pointer) { + sem := (*semaphore)(semphr) + close(*sem) +} + +//export espradio_wifi_thread_semphr_get +func espradio_wifi_thread_semphr_get() unsafe.Pointer { + if wifiSemaphore == nil { + wifiSemaphore = make(semaphore, 1) + } + return unsafe.Pointer(&wifiSemaphore) +} + type queueElementType [8]byte -// TODO: I think the return type results in undefined behavior (but it's still a -// pointer so I hope LLVM won't use that fact). -// //export espradio_wifi_create_queue func espradio_wifi_create_queue(queue_len, item_size int) chan queueElementType { - if item_size > len(queueElementType{}) { - panic("espradio: queue item_size too large") + if item_size != len(queueElementType{}) { + panic("espradio: unexpected queue item_size") } return make(chan queueElementType, item_size) } + +//export espradio_wifi_delete_queue +func espradio_wifi_delete_queue(queue chan queueElementType) { + // We can't really delete a channel, but we can close it. + close(queue) +} + +//export espradio_queue_recv +func espradio_queue_recv(queue chan queueElementType, item unsafe.Pointer, block_time_tick uint32) int32 { + // This is xQueueReceive. + if block_time_tick != C.OSI_FUNCS_TIME_BLOCKING { + panic("espradio: todo: queue_recv with timeout") + } + *(*[8]byte)(item) = <-queue + return 1 +} + +//export espradio_queue_send +func espradio_queue_send(queue chan queueElementType, item unsafe.Pointer, block_time_tick uint32) int32 { + // This is xQueueSend. + if block_time_tick != C.OSI_FUNCS_TIME_BLOCKING { + duration := time.Duration(ticksToMilliseconds(block_time_tick)) * time.Millisecond + // TODO: reuse the timer to avoid allocating a new timer on each queue send + select { + case <-time.After(duration): + return 0 + case queue <- *(*[8]byte)(item): + return 1 + } + } + queue <- *(*[8]byte)(item) + return 1 +}