Files
drivers/espradio/sync.go
T
Ayke van Laethem 9ca474466d WIP continuing
This commit now requires tinygo_task_current which I've patched in my
local TinyGo build.
2024-01-28 14:43:03 +01:00

164 lines
4.6 KiB
Go

package espradio
// Various functions related to locks, mutexes, semaphores, and queues.
/*
#include "include.h"
*/
import "C"
import (
"sync"
"sync/atomic"
"time"
"unsafe"
)
// Use a single fake spinlock. This is also how the Rust port does it.
var fakeSpinLock uint8
//export espradio_spin_lock_create
func espradio_spin_lock_create() unsafe.Pointer {
return unsafe.Pointer(&fakeSpinLock)
}
//export espradio_spin_lock_delete
func espradio_spin_lock_delete(lock unsafe.Pointer) {
}
// Use a global array of mutexes, because the binary blobs don't need that many.
var mutexes [2]sync.Mutex
var mutexIndex uint32
//export espradio_recursive_mutex_create
func espradio_recursive_mutex_create() unsafe.Pointer {
// Allocate a mutex from the global array.
// If the radio needs more mutexes, this will result in an index out of
// range panic (hopefully including a source location, if using
// `tinygo flash -monitor`).
newIndex := atomic.AddUint32(&mutexIndex, 1)
mutex := &mutexes[newIndex-1]
return unsafe.Pointer(mutex)
}
//export espradio_mutex_lock
func espradio_mutex_lock(cmut unsafe.Pointer) int32 {
// This is xSemaphoreTake with an infinite timeout in ESP-IDF. Therefore,
// just lock the mutex and return true.
// TODO: recursive locking. See:
// https://www.freertos.org/RTOS-Recursive-Mutexes.html
// For that we need to track the current goroutine - or maybe just whether
// we're inside a special goroutine like the timer goroutine.
mut := (*sync.Mutex)(cmut)
mut.Lock()
return 1
}
//export espradio_mutex_unlock
func espradio_mutex_unlock(cmut unsafe.Pointer) int32 {
// Note: this is xSemaphoreGive in the ESP-IDF, which doesn't panic when
// unlocking fails but rather returns false.
mut := (*sync.Mutex)(cmut)
mut.Unlock()
return 1
}
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
//export espradio_wifi_create_queue
func espradio_wifi_create_queue(queue_len, item_size int) chan queueElementType {
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
}