Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • card10/firmware
  • annejan/firmware
  • astro/firmware
  • fpletz/firmware
  • gerd/firmware
  • fleur/firmware
  • swym/firmware
  • l/firmware
  • uberardy/firmware
  • wink/firmware
  • madonius/firmware
  • mot/firmware
  • filid/firmware
  • q3k/firmware
  • hauke/firmware
  • Woazboat/firmware
  • pink/firmware
  • mossmann/firmware
  • omniskop/firmware
  • zenox/firmware
  • trilader/firmware
  • Danukeru/firmware
  • shoragan/firmware
  • zlatko/firmware
  • sistason/firmware
  • datenwolf/firmware
  • bene/firmware
  • amedee/firmware
  • martinling/firmware
  • griffon/firmware
  • chris007/firmware
  • adisbladis/firmware
  • dbrgn/firmware
  • jelly/firmware
  • rnestler/firmware
  • mh/firmware
  • ln/firmware
  • penguineer/firmware
  • monkeydom/firmware
  • jens/firmware
  • jnaulty/firmware
  • jeffmakes/firmware
  • marekventur/firmware
  • pete/firmware
  • h2obrain/firmware
  • DooMMasteR/firmware
  • jackie/firmware
  • prof_r/firmware
  • Draradech/firmware
  • Kartoffel/firmware
  • hinerk/firmware
  • abbradar/firmware
  • JustTB/firmware
  • LuKaRo/firmware
  • iggy/firmware
  • ente/firmware
  • flgr/firmware
  • Lorphos/firmware
  • matejo/firmware
  • ceddral7/firmware
  • danb/firmware
  • joshi/firmware
  • melle/firmware
  • fitch/firmware
  • deurknop/firmware
  • sargon/firmware
  • markus/firmware
  • kloenk/firmware
  • lucaswerkmeister/firmware
  • derf/firmware
  • meh/firmware
  • dx/card10-firmware
  • torben/firmware
  • yuvadm/firmware
  • AndyBS/firmware
  • klausdieter1/firmware
  • katzenparadoxon/firmware
  • xiretza/firmware
  • ole/firmware
  • techy/firmware
  • thor77/firmware
  • TilCreator/firmware
  • fuchsi/firmware
  • dos/firmware
  • yrlf/firmware
  • PetePriority/firmware
  • SuperVirus/firmware
  • sur5r/firmware
  • tazz/firmware
  • Alienmaster/firmware
  • flo_h/firmware
  • baldo/firmware
  • mmu_man/firmware
  • Foaly/firmware
  • sodoku/firmware
  • Guinness/firmware
  • ssp/firmware
  • led02/firmware
  • Stormwind/firmware
  • arist/firmware
  • coon/firmware
  • mdik/firmware
  • pippin/firmware
  • royrobotiks/firmware
  • zigot83/firmware
  • mo_k/firmware
106 results
Select Git revision
Loading items
Show changes
Showing
with 2028 additions and 12 deletions
os_sources = files(
'config.c',
'log.c',
'mutex.c',
'panic.c',
'work_queue.c',
)
#include "os/mutex.h"
#include <assert.h>
void _mutex_create(struct mutex *m, const char *name)
{
/* Assert that the mutex has not been initialized already */
assert(m->name == NULL);
/*
* The name is just the parameter stringified which is almost always a
* pointer. If it is, skip over the '&' because it adds no value as
* part of the name.
*/
if (name[0] == '&') {
m->name = &name[1];
} else {
m->name = name;
}
m->_rtos_mutex = xSemaphoreCreateMutexStatic(&m->_rtos_mutex_data);
}
void mutex_lock(struct mutex *m)
{
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
return;
}
int ret = xSemaphoreTake(m->_rtos_mutex, portMAX_DELAY);
/* Ensure locking was actually successful */
assert(ret == pdTRUE);
}
bool mutex_trylock(struct mutex *m)
{
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
return true;
}
int ret = xSemaphoreTake(m->_rtos_mutex, 0);
return ret == pdTRUE;
}
void mutex_assert_locked(struct mutex *m)
{
assert(mutex_get_owner(m) == xTaskGetCurrentTaskHandle());
}
void mutex_unlock(struct mutex *m)
{
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
return;
}
/* Ensure that only the owner can unlock a mutex */
mutex_assert_locked(m);
int ret = xSemaphoreGive(m->_rtos_mutex);
/*
* Ensure that unlocking was successful; that is, the mutex must have
* been acquired previously (no multiple unlocks).
*/
assert(ret == pdTRUE);
}
TaskHandle_t mutex_get_owner(struct mutex *m)
{
return xSemaphoreGetMutexHolder(m->_rtos_mutex);
}
#ifndef _MUTEX_H
#define _MUTEX_H
#ifndef __SPHINX_DOC
/* Some headers are not recognized by hawkmoth for some odd reason */
#include <stddef.h>
#include <stdbool.h>
#include "FreeRTOS.h"
#include "semphr.h"
#else
typedef unsigned int size_t;
typedef _Bool bool;
typedef void *TaskHandle_t;
typedef void *SemaphoreHandle_t;
typedef int StaticSemaphore_t;
#endif /* __SPHINX_DOC */
/**
* Mutex data type.
*/
struct mutex {
/* Name of this mutex, kept for debugging purposes. */
const char *name;
/* FreeRTOS mutex data structures. */
SemaphoreHandle_t _rtos_mutex;
StaticSemaphore_t _rtos_mutex_data;
};
/**
* Create a new mutex.
*
* Call this function as early as possible, in an obvious location so it is easy
* to find. Mutexes should be defined statically so they stay alive for the
* entire run-time of the firmware.
*/
#define mutex_create(mutex) _mutex_create(mutex, #mutex)
void _mutex_create(struct mutex *m, const char *name);
/**
* Lock a mutex.
*
* If the mutex is held by another task, :c:func:`mutex_lock` will block the
* current task until the mutex is unlocked.
*
* .. warning::
*
* This function is **not** safe to use in a timer!
*/
void mutex_lock(struct mutex *m);
/**
* Try locking a mutex.
*
* If the mutex is currently locked by another task, :c:func:`mutex_trylock`
* will return ``false`` immediately. If the attmept to lock was successful, it
* will return ``true``.
*
* This funciton is safe for use in timers.
*/
bool mutex_trylock(struct mutex *m);
/**
* Unlock a mutex.
*
* You **must** call this function from the same task which originally locked
* the mutex.
*/
void mutex_unlock(struct mutex *m);
/**
* Assert that the current task is holding a mutex lock.
*
* If a function requires a certain mutex to be held for safe operation,
* but does not take care of the locking itself, :c:func:`mutex_assert_locked`
* can be used to assert correct caller behavior.
*/
void mutex_assert_locked(struct mutex *m);
/**
* Get the current owner of the mutex.
*
* Returns the task-handle of the task currently holding the mutex. If the
* mutex is unlocked, ``NULL`` is returned.
*/
TaskHandle_t mutex_get_owner(struct mutex *m);
#endif /* _MUTEX_H */
/*
* Panic
* =====
*
* Under some conditions the firmware should crash and reboot automatically.
* This module provides the necessary facilities to do so.
*
* Note that a panic should indicate **only** logic-errors in the firmware or
* unrecoverable hardware conditions.
*/
#include "os/core.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "drivers/display/epic_ctx.h"
#include "drivers/display/lcd.h"
#include "card10.h"
#include "card10-version.h"
#include "LCD_Driver.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
static void faultsplash(const char *msg);
void __attribute__((noreturn)) panic(const char *format, ...)
{
/* Turn off interrupts. We won't get back from here anyway. */
__asm volatile("cpsid i" ::: "memory");
/*
* Turn off asynchronous printing because that won't ever work from
* here ...
*/
serial_return_to_synchronous();
printf("\x1b[31;1m --- SYSTEM PANIC ---\n"
"\x1b[0;31m --- ---\n"
" --- ---\n"
"\x1b[0m A fatal error occured:\n \x1b[1m");
va_list ap;
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
printf("\n"
"\x1b[0m\n"
" Firmware Version:\n"
"\x1b[35m %s\n",
CARD10_VERSION);
printf("\x1b[0m\n"
" Stack Trace:\n"
"\x1b[36m %p\n",
__builtin_return_address(0));
printf("\x1b[33m\n"
" Please report this error to the card10 firmware team!\n"
"\x1b[0m -> https://git.card10.badge.events.ccc.de/card10/firmware/issues/new <-\n"
"\x1b[31m --- ====== ===== ---\x1b[0m\n");
char faultsplash_buffer[14 * 4];
va_start(ap, format);
vsnprintf(faultsplash_buffer, sizeof(faultsplash_buffer), format, ap);
va_end(ap);
faultsplash(faultsplash_buffer);
for (int i = 0; i < 96000000; i++) {
__asm volatile("nop");
}
card10_reset();
}
void __attribute__((noreturn)) __assert_func(
const char *file, int line, const char *func, const char *failedexpr
) {
panic("Assertion failure:\n"
" \"%s\"\n"
" failed in \"%s:%d\",\n"
" function: %s()",
failedexpr,
file,
line,
func);
}
static const unsigned char faultsplash_rle[] = {
0x7f, 0x50, 0x83, 0x0f, 0x82, 0x7f, 0x0d, 0x83, 0x0f, 0x82, 0x7f, 0x1d,
0x82, 0x7f, 0x1f, 0x82, 0x7f, 0x1f, 0x82, 0x7f, 0x12, 0x82, 0x09, 0x82,
0x7f, 0x14, 0x82, 0x09, 0x82, 0x7f, 0x09, 0x82, 0x11, 0x83, 0x7f, 0x0b,
0x82, 0x11, 0x83, 0x7f, 0x0f, 0x82, 0x07, 0x82, 0x7f, 0x16, 0x82, 0x07,
0x82, 0x7f, 0x16, 0x82, 0x07, 0x82, 0x7f, 0x1a, 0x83, 0x7f, 0x1e, 0x83,
0x7f, 0x0a, 0x8b, 0x17, 0x82, 0x02, 0x82, 0x02, 0x83, 0x73, 0x8c, 0x16,
0x82, 0x02, 0x82, 0x02, 0x83, 0x72, 0x81, 0x0c, 0x84, 0x07, 0x85, 0x7f,
0x03, 0x82, 0x0c, 0x84, 0x07, 0x86, 0x7f, 0x02, 0x82, 0x0c, 0x84, 0x07,
0x86, 0x7f, 0x82, 0x12, 0x87, 0x7f, 0x06, 0x82, 0x12, 0x87, 0x7f, 0x06,
0x82, 0x24, 0x82, 0x78, 0x82, 0x24, 0x82, 0x78, 0x82, 0x24, 0x82, 0x78,
0x82, 0x16, 0x83, 0x04, 0x82, 0x07, 0x82, 0x76, 0x82, 0x16, 0x83, 0x04,
0x82, 0x07, 0x82, 0x70, 0x8f, 0x0d, 0x82, 0x12, 0x82, 0x6e, 0x8f, 0x0d,
0x82, 0x12, 0x82, 0x6e, 0x8f, 0x0b, 0x82, 0x09, 0x82, 0x0b, 0x82, 0x6c,
0x8f, 0x0b, 0x82, 0x09, 0x82, 0x0b, 0x82, 0x6c, 0x8f, 0x0b, 0x82, 0x09,
0x82, 0x0b, 0x82, 0x6c, 0x8f, 0x7f, 0x12, 0x8f, 0x7f, 0x0d, 0x98, 0x12,
0x82, 0x74, 0x98, 0x12, 0x82, 0x70, 0xa1, 0x7f, 0xa1, 0x7f, 0xa1, 0x7f,
0xa1, 0x7f, 0xa1, 0x7d, 0xa5, 0x7b, 0xa5, 0x7b, 0xa5, 0x7b, 0xa5, 0x7a,
0xa6, 0x78, 0x89, 0x02, 0x9f, 0x76, 0x89, 0x02, 0x9f, 0x76, 0xaa, 0x76,
0xaa, 0x76, 0x87, 0x02, 0xa1, 0x76, 0x87, 0x02, 0xa1, 0x76, 0x87, 0x02,
0xa1, 0x76, 0x87, 0x02, 0xa1, 0x76, 0x87, 0x02, 0xa1, 0x76, 0x87, 0x02,
0xa1, 0x76, 0x87, 0x02, 0xa1, 0x76, 0xaa, 0x76, 0xaa, 0x76, 0xaa, 0x76,
0x89, 0x02, 0x9f, 0x76, 0x89, 0x02, 0x9f, 0x79, 0xa5, 0x7b, 0xa5, 0x7b,
0xa5, 0x7b, 0x8b, 0x02, 0x98, 0x7b, 0x8b, 0x02, 0x98, 0x7d, 0xa1, 0x7f,
0xa1, 0x7f, 0xa1, 0x7f, 0xa1, 0x7f, 0xa1, 0x7f, 0x04, 0x98, 0x7f, 0x09,
0x98, 0x7f, 0x0e, 0x8f, 0x7f, 0x12, 0x8f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x06
};
static void faultsplash(const char *msg)
{
LCD_SetBacklight(100);
size_t idx = 0;
for (size_t i = 0; i < sizeof(faultsplash_rle); i++) {
uint8_t color = (faultsplash_rle[i] & 0x80) ? 0xff : 0x00;
uint8_t length = faultsplash_rle[i] & 0x7f;
memset(&epicardium_ctx_fb[idx], color, length * 2);
idx += length * 2;
}
lcd_write_fb(epicardium_ctx_fb);
/* make sure we get a clean graphics ctx */
disp_ctx_reinit();
ctx_text_baseline(epicardium_ctx, CTX_TEXT_BASELINE_TOP);
ctx_font_size(epicardium_ctx, 20.0f);
ctx_move_to(epicardium_ctx, 80.0f, 8.0f);
ctx_rgba8(epicardium_ctx, 0xff, 0x00, 0x00, 0xff);
ctx_text(epicardium_ctx, "Panic");
ctx_font_size(epicardium_ctx, 12.0f);
ctx_rgba8(epicardium_ctx, 197, 197, 197, 0xff);
size_t len = strlen(msg);
char buf[15] = { 0 };
float offset = 34.0f;
for (size_t i = 0; i < len; i += 14) {
strncpy(buf, &msg[i], 14);
ctx_move_to(epicardium_ctx, 52.0f, offset);
ctx_text(epicardium_ctx, buf);
offset += 12.0f;
}
lcd_write_fb(epicardium_ctx_fb);
}
#include "epicardium.h"
#include "os/core.h"
#include "os/work_queue.h"
#include "FreeRTOS.h"
#include "queue.h"
struct work {
void (*func)(void *data);
void *data;
};
static QueueHandle_t work_queue;
static uint8_t buffer[sizeof(struct work) * WORK_QUEUE_SIZE];
static StaticQueue_t work_queue_data;
void workqueue_init(void)
{
work_queue = xQueueCreateStatic(
WORK_QUEUE_SIZE, sizeof(struct work), buffer, &work_queue_data
);
}
int workqueue_schedule(void (*func)(void *data), void *data)
{
struct work work = { func, data };
if (xQueueSend(work_queue, &work, 0) != pdTRUE) {
/* Likely full */
LOG_WARN("workqueue", "could not schedule %p(%p)", func, data);
return -EAGAIN;
}
return 0;
}
void vWorkQueueTask(void *pvParameters)
{
struct work work;
workqueue_init();
while (1) {
if (xQueueReceive(work_queue, &work, portMAX_DELAY) == pdTRUE) {
if (work.func) {
work.func(work.data);
}
}
}
}
#ifndef _WORK_QUEUE_H
#define _WORK_QUEUE_H
#define WORK_QUEUE_SIZE 20
void workqueue_init(void);
int workqueue_schedule(void (*func)(void *data), void *data);
void vWorkQueueTask(void *pvParameters);
#endif
...@@ -6,33 +6,146 @@ ...@@ -6,33 +6,146 @@
#include "task.h" #include "task.h"
#include "api/dispatcher.h" #include "api/dispatcher.h"
#include "modules/log.h" #include "user_core/user_core.h"
#include "os/core.h"
#include "drivers/drivers.h"
#include "modules/modules.h"
#define BB_CLK_RATE_HZ 1600000
#include "usb.h"
#include "mxc_sys.h"
#include "wut.h"
#include "wsf_types.h"
#include "wsf_os.h"
#include "sch_api_ble.h"
#include "bb_drv.h"
#include "card10.h" #include "card10.h"
extern TaskHandle_t dispatcher_task_id; #define US_TO_BBTICKS(x) (((x) * (BB_CLK_RATE_HZ / 100000) + 9) / 10)
#define MIN(a, b) (a < b) ? a : b
#define MIN_SLEEP_TIME_MS 1
static int32_t ble_sleep_ticks(void)
{
uint32_t nextDbbEventDue;
bool_t dueValid = SchBleGetNextDueTime(&nextDbbEventDue);
int sleep_ticks = nextDbbEventDue - BbDrvGetCurrentTime();
if (dueValid) {
if (sleep_ticks > 0) {
uint32_t bb_idle = BB_TICKS_TO_US(sleep_ticks) / 1000;
return bb_idle;
} else {
return 0;
}
} else {
return -1;
}
}
/* /*
* This hook is called before FreeRTOS enters tickless idle. * This hook is called before FreeRTOS enters tickless idle.
*/ */
void pre_idle_sleep(TickType_t xExpectedIdleTime) void pre_idle_sleep(TickType_t xExpectedIdleTime)
{ {
if (xExpectedIdleTime > 0) { if (xExpectedIdleTime > 0 &&
(CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0) {
/* /*
* WFE because the other core should be able to notify * WFE because the other core should be able to notify
* epicardium if it wants to issue an API call. * epicardium if it wants to issue an API call.
*/ */
/* /* If the other core is waiting for a call to return and we are able
* TODO: Ensure this is actually correct and does not have any * to sleep, reduce the system clock to save energy. */
* race conditions. if (xExpectedIdleTime >= pdMS_TO_TICKS(MIN_SLEEP_TIME_MS) &&
*/ api_dispatcher_call_pending() && wsfOsReadyToSleep()) {
if ((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0) { uint32_t ms = xExpectedIdleTime;
if (ble_is_enabled()) {
int32_t ble_idle = ble_sleep_ticks();
if (ble_idle >= 0) {
ms =
MIN(xExpectedIdleTime,
(uint32_t)ble_idle);
}
}
// Us the WUT only if we can sleep a significant amount of time
if (ms >= MIN_SLEEP_TIME_MS) {
/* Initialize Wakeup timer */
WUT_Init(WUT_PRES_1);
wut_cfg_t wut_cfg;
wut_cfg.mode = WUT_MODE_COMPARE;
wut_cfg.cmp_cnt = 0xFFFFFFFF;
WUT_Config(&wut_cfg);
WUT_Enable();
/* Enable WUT as a wakup source */
NVIC_EnableIRQ(WUT_IRQn);
uint32_t targetTick;
targetTick = WUT_GetCount();
targetTick +=
((uint64_t)(ms)*SYS_WUT_GetFreq() /
1000);
WUT_SetCompare(targetTick);
/* Stop SysTick */
uint32_t val = SysTick->VAL;
SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk);
if (usb_get_status() & MAXUSB_STATUS_VBUS_ON) {
/* Need to stay on 96 MHz. USB serial becomes
* unstable otherwise. Don't know why. */
//SYS_Clock_Select(SYS_CLOCK_HIRC, NULL);
} else {
MXC_GCR->clkcn |=
MXC_S_GCR_CLKCN_PSC_DIV4;
SYS_Clock_Select(SYS_CLOCK_HIRC, NULL);
SYS_ClockSourceDisable(
SYS_CLOCK_HIRC96
);
}
disp_update_backlight_clock();
__asm volatile("dsb" ::: "memory"); __asm volatile("dsb" ::: "memory");
__asm volatile("wfe"); __asm volatile("wfe");
__asm volatile("isb"); __asm volatile("isb");
MXC_GCR->clkcn &= ~(MXC_S_GCR_CLKCN_PSC_DIV4);
SYS_Clock_Select(SYS_CLOCK_HIRC96, NULL);
#if 0
/* Allow to serve high priority interrupts before
* doing the lengthy xTaskIncrementTick loop. */
portDISABLE_INTERRUPTS();
__set_PRIMASK(0);
__set_PRIMASK(1);
portENABLE_INTERRUPTS();
#endif
disp_update_backlight_clock();
SYS_ClockSourceDisable(SYS_CLOCK_HIRC);
SysTick->LOAD = val;
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
ms = (WUT_GetCount() * 1000) /
SYS_WUT_GetFreq();
for (uint32_t t = 0; t < ms; t++) {
xTaskIncrementTick();
}
return;
} }
} }
/* Fall back to light sleep with clocks on if we can't
* sleep long enough for the WUT */
__asm volatile("dsb" ::: "memory");
__asm volatile("wfe");
__asm volatile("isb");
}
} }
/* /*
...@@ -118,5 +231,5 @@ void vApplicationGetTimerTaskMemory( ...@@ -118,5 +231,5 @@ void vApplicationGetTimerTaskMemory(
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName)
{ {
LOG_CRIT("rtos", "Task \"%s\" overflowed stack!", pcTaskName); panic("Task \"%s\" overflowed stack!", pcTaskName);
} }
/**
*
* epicardium-specific implemnetation of cdcacm device
* services CDCACM API
*/
#include "usb/epc_usb.h"
#include "usb/cdcacm.h"
#include "usb/descriptors.h"
#include <stdio.h>
#include <stddef.h>
#include <errno.h>
#include "usb.h"
#include "usb_event.h"
#include "cdc_acm.h"
#include "os/core.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "FreeRTOS.h"
#include "task.h"
static volatile int lockup_disable = 0;
static inline struct config_descriptor_cdcacm *
descriptors(struct esb_config *self)
{
return self->descriptors->cdcacm;
}
/******************************************************************************/
static volatile int usb_read_complete; // SWYM: only ever written to
static int cb_acm_read_ready(void)
{
usb_read_complete = 1;
return 0;
}
static int cb_acm_connected(void)
{
lockup_disable = 0;
return 0;
}
int esb_cdc_init(struct esb_config *self)
{
LOG_DEBUG("cdcacm", "init");
struct config_descriptor_cdcacm *dsc = descriptors(self);
usb_read_complete = 0;
int ret = acm_init(&dsc->comm_interface);
acm_register_callback(
ACM_CB_READ_READY,
cb_acm_read_ready); //SWYM: actually not needed
acm_register_callback(ACM_CB_CONNECTED, cb_acm_connected);
return ret;
}
int esb_cdc_configure(struct esb_config *self)
{
struct config_descriptor_cdcacm *dsc = descriptors(self);
//acm_configure does not keep the amc_cfg_t pointer around
//so stack-local lifetime is fine
acm_cfg_t acm_cfg = {
.in_ep = dsc->endpoint_in.bEndpointAddress & 0x0f,
.in_maxpacket = MXC_USBHS_MAX_PACKET,
.out_ep = dsc->endpoint_out.bEndpointAddress,
.out_maxpacket = MXC_USBHS_MAX_PACKET,
.notify_ep = dsc->endpoint_notify.bEndpointAddress & 0x0f,
.notify_maxpacket = MXC_USBHS_MAX_PACKET,
};
LOG_DEBUG(
"cdcacm",
"configure, endpoints %d,%d,%d",
acm_cfg.in_ep,
acm_cfg.out_ep,
acm_cfg.notify_ep
);
return acm_configure(&acm_cfg);
}
int esb_cdc_deconfigure(struct esb_config *self)
{
LOG_DEBUG("cdcacm", "deconfigure");
return acm_deconfigure();
}
int cdcacm_num_read_avail(void)
{
return acm_canread();
}
uint8_t cdcacm_read(void)
{
while (acm_canread() <= 0) {
}
uint8_t buf;
acm_read(&buf, 1);
return buf;
}
void cdcacm_write(uint8_t *data, int len)
{
if (acm_present() && !lockup_disable) {
int ret = acm_write(data, len);
if (ret < 0) {
LOG_ERR("cdcacm", "fifo lockup detected");
lockup_disable = 1;
} else if (ret != len) {
LOG_WARN(
"cdcacm", "write length mismatch, got %d", ret
);
}
}
}
/******************************************************************************/
extern TaskHandle_t serial_task_id;
void USB_IRQHandler(void)
{
usb_event_handler();
if (serial_task_id != NULL) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(
serial_task_id,
SERIAL_READ_NOTIFY,
eSetBits,
&xHigherPriorityTaskWoken
);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
#ifndef CDCACM_H
#define CDCACM_H
#include "usb/epc_usb.h"
// callbacks for esb_config
int esb_cdc_init(struct esb_config* self);
int esb_cdc_configure(struct esb_config* self);
int esb_cdc_deconfigure(struct esb_config* self);
int cdcacm_num_read_avail(void);
uint8_t cdcacm_read(void);
void cdcacm_write(uint8_t *data, int len);
#endif
#ifndef _DESCRIPTORS_H_
#define _DESCRIPTORS_H_
#include <stdint.h>
#include "usb.h"
#define ESB_ENDPOINT_MSC_IN 2
#define ESB_ENDPOINT_MSC_OUT 1
#define ESB_ENDPOINT_CDCACM_NOTIFY 3
#define ESB_ENDPOINT_CDCACM_IN 2
#define ESB_ENDPOINT_CDCACM_OUT 1
/* device types */
#define DT_DEVICE 0x01
#define DT_CONFIG 0x02
#define DT_INTERFACE 0x04
#define DT_ENDPOINT 0x05
#define DT_FUNCTIONAL 0x24
/* interface classes */
#define CLS_UNSPECIFIED 0x00
#define CLS_COMM 0x02
#define CLS_MASS_STOR 0x08
#define CLS_DATA 0x0a
/* sub-classes */
#define SCLS_NONE 0x00
#define SCLS_ACM 0x02
#define SCLS_SCSI_CMDS 0x06
/* interface protocols */
#define PROT_AT_CMDS 0x01
#define PROT_BULK_TRANS 0x50
/* endpoint attributes */
#define ATTR_BULK 0x02
#define ATTR_INTERRUPT 0x03
#define VNDR_MAXIM 0x0B6A
#if defined(__GNUC__)
#define PACKED_STRUCT struct __attribute__((packed))
#else
#define PACKED_STRUCT __packed struct
#endif
PACKED_STRUCT
config_descriptor_cdcacm {
usb_configuration_descriptor_t config;
usb_interface_descriptor_t comm_interface;
uint8_t header_functional[5];
uint8_t call_management[5];
uint8_t acm_functional[4];
uint8_t union_functional[5];
usb_endpoint_descriptor_t endpoint_notify;
usb_interface_descriptor_t data_interface;
usb_endpoint_descriptor_t endpoint_out;
usb_endpoint_descriptor_t endpoint_in;
};
PACKED_STRUCT
config_descriptor_msc {
usb_configuration_descriptor_t config;
usb_interface_descriptor_t msc_interface;
usb_endpoint_descriptor_t endpoint_out;
usb_endpoint_descriptor_t endpoint_in;
};
struct esb_device_descriptors {
usb_device_descriptor_t* device;
union {
usb_configuration_descriptor_t* config;
struct config_descriptor_cdcacm* cdcacm;
struct config_descriptor_msc* msc;
};
};
#endif /* _DESCRIPTORS_H_ */
/**
*
*
* Core of the USB implementation:
*
* - device independent
* - handles setup of MAXUSB stack
* - handles events and state of the MAXUSB stack
*
* Also contains definitions of device-independent String descriptors
*
*
*
*
*
* swym's USB ramblings:
*
* setting up USB basically consists of two parts:
*
* - set up descriptors
* - initialize the corresponding MAXUSB stacks - acm, msc...
*
* at the moment, the descriptors are statically configured in
* descriptors.h several descriptors point to enumeration descriptors
* via iFooBar indices. Those enumeration descriptors are registered
* via enum_register_descriptor(type, desc, idx), where the idx passed
* corresponds to the iFooBar index mentioned above.
*
* There are several callbacks, some of which do not perform anything meaningful
* at the moment. For example usb_write_callback sets a global flag that is never
* read from. These will be removed later on.
*
* The initialization routines of acm & msc refer to the descriptors from which,
* among other things, they get their enpoint IDs. These are hard-coded right now
* but it would make sense to allocate these IDs at runtime. There is, AFAICT, no
* reason for these endpoint IDs to be 3, 1, 2 in the CDCACM case for example.
*
* Allocating those at runtime would also make the setup less rigid: we can have all
* combinations of {cdcacm, storage} interfaces without the awkward descriptor hackery
* in epc_usb_init.
*
* To generalize even further, the descriptors could be malloced. The total length of
* the descriptor structure can easily be derived from the number of interfaces to be
* initialized. The memory allocated will then be split up (with alignment of the single
* descriptor structs taken care of) in a simple bump-allocator strategy on demand.
*
*
*/
#include "usb/epc_usb.h"
#include <assert.h>
#include <stdio.h>
#include <stddef.h>
#include <FreeRTOS.h>
#include <task.h>
#include <timers.h>
#include "mxc_config.h"
#include "mxc_sys.h"
#include "mxc_delay.h"
// MAXUSB includes
#include "usb.h"
#include "usb_event.h"
#include "enumerate.h"
#include "msc.h"
#include "cdc_acm.h"
#include "usb/descriptors.h"
#include "os/core.h"
#include "fs/filesystem.h"
//#define USE_REMOTE_WAKE_ENABLE
/***** Function Prototypes *****/
static int cb_usb_setconfig(usb_setup_pkt *sud, void *cbdata);
#ifdef USE_REMOTE_WAKE_ENABLE
static int cb_usb_setfeature(usb_setup_pkt *sud, void *cbdata);
static int cb_usb_clrfeature(usb_setup_pkt *sud, void *cbdata);
#endif
static int cb_usb_event(maxusb_event_t evt, void *data);
static int cb_usb_shutdown();
static int cb_usb_init();
static void usb_app_sleep(void);
static void usb_app_wakeup(void);
/* needed for usb_opts. mxc_delay() takes unsigned long, so can't use it directly */
static void delay_us(unsigned int usec);
static void device_deinit();
static bool device_deconfigure();
static int device_configure();
volatile int suspended;
#ifdef USE_REMOTE_WAKE_ENABLE
int remote_wake_en; //SWYM: unused, only written to!
#endif
static StaticTimer_t x;
static TimerHandle_t timerWakeup = NULL;
static void cb_timerReset(TimerHandle_t t);
static struct esb_config s_device = { 0 };
__attribute__((aligned(4))) uint8_t lang_id_desc[] = {
0x04, /* bLength */
0x03, /* bDescriptorType */
0x09,
0x04 /* bString = wLANGID (see usb_20.pdf 9.6.7 String) */
};
__attribute__((aligned(4))) uint8_t mfg_id_desc[] = {
0x22, /* bLength */
0x03, /* bDescriptorType */
'M', 0, 'a', 0, 'x', 0, 'i', 0, 'm', 0, ' ', 0, 'I', 0, 'n', 0,
't', 0, 'e', 0, 'g', 0, 'r', 0, 'a', 0, 't', 0, 'e', 0, 'd', 0,
};
__attribute__((aligned(4))) uint8_t prod_id_desc[] = {
0x24, /* bLength */
0x03, /* bDescriptorType */
'C', 0, 'A', 0, 'R', 0, 'D', 0, '1', 0, '0', 0, ' ', 0, 'U', 0, 'S', 0,
'B', 0, ' ', 0, 'G', 0, 'A', 0, 'D', 0, 'G', 0, 'E', 0, 'T', 0,
};
/* Not currently used (see device descriptor), but could be enabled if desired */
__attribute__((aligned(4)))
uint8_t serial_id_desc[] = { 0x14, /* bLength */
0x03, /* bDescriptorType */
'c', 0, 'a', 0, 'a', 0, 'a', 0, 'a',
0, 'd', 0, '1', 0, '0', 0, '0', 0 };
int cb_usb_init()
{
const sys_cfg_usbhs_t sys_usbhs_cfg = NULL;
LOG_DEBUG("usb", "init");
return SYS_USBHS_Init(&sys_usbhs_cfg);
}
volatile bool shutDownCompleted;
void do_usb_shutdown()
{
LOG_DEBUG("usb", "shutting down...");
shutDownCompleted = false;
usb_shutdown();
for (int i = 0; i < 10000 && !shutDownCompleted; ++i) {
}
}
int cb_usb_shutdown()
{
SYS_USBHS_Shutdown();
SYS_Reset_Periph(SYS_RESET_USB);
device_deinit();
LOG_DEBUG("usb", "shutdown complete");
shutDownCompleted = true;
return 0;
}
/* User-supplied function to delay usec micro-seconds */
static void delay_us(unsigned int usec)
{
/* mxc_delay() takes unsigned long, so can't use it directly */
mxc_delay(usec);
}
static void device_deinit()
{
esb_cfg_handler deinit = s_device.deinit;
s_device.deinit = NULL;
s_device.configure = NULL;
s_device.deconfigure = NULL;
if (deinit) {
LOG_DEBUG("usb", "deinit");
deinit(&s_device);
}
}
//de-configure last device, if any
static volatile bool s_configured = false;
static bool device_deconfigure()
{
if (s_configured) {
s_configured = false;
if (s_device.deconfigure) {
LOG_DEBUG("usb", "deconfigure");
enum_clearconfig();
s_device.deconfigure(&s_device);
return true;
}
}
return false;
}
static int device_configure()
{
if (!s_configured && s_device.configure) {
s_configured = true;
LOG_DEBUG("usb", "configure");
return s_device.configure(&s_device);
}
return 0;
}
static volatile bool s_connected = false;
static void disconnect()
{
if (s_connected) {
LOG_DEBUG("usb", "disconnect");
s_connected = false;
usb_disconnect();
// SYS_Reset_Periph(SYS_RESET_USB);
mxc_delay(10000);
}
}
static void connect()
{
s_connected = true;
usb_connect();
}
static volatile bool s_initialized = false;
void esb_deinit(void)
{
if (s_initialized) {
s_initialized = false;
disconnect();
device_deconfigure();
do_usb_shutdown();
device_deinit();
}
}
int esb_init(struct esb_config *cfg)
{
esb_deinit();
if (cfg == NULL) {
return 0;
}
if (timerWakeup == NULL) {
timerWakeup = xTimerCreateStatic(
"timerWakeup", /* name */
pdMS_TO_TICKS(50), /* period/time */
pdFALSE, /* auto reload */
NULL, /* timer ID */
cb_timerReset,
&x);
}
maxusb_cfg_options_t usb_opts;
/* Initialize state */
suspended = 0;
#ifdef USE_REMOTE_WAKE_ENABLE
remote_wake_en = 0;
#endif
/* Start out in full speed */
usb_opts.enable_hs = 0;
usb_opts.delay_us = delay_us;
usb_opts.init_callback = cb_usb_init;
usb_opts.shutdown_callback = cb_usb_shutdown;
/* Initialize the usb module */
if (usb_init(&usb_opts) != 0) {
LOG_ERR("usb", "usb_init() failed");
return -EIO;
}
/* Initialize the enumeration module */
if (enum_init() != 0) {
do_usb_shutdown();
LOG_ERR("usb", "enum_init() failed");
return -EIO;
}
LOG_INFO("usb", "initializing device %s", cfg->name);
if (cfg->init(cfg)) {
enum_clearconfig();
do_usb_shutdown();
LOG_ERR("usb", "device init failed");
return -EIO;
}
s_initialized = true;
s_device = *cfg;
enum_register_descriptor(
ENUM_DESC_DEVICE, (uint8_t *)cfg->descriptors->device, 0
);
enum_register_descriptor(
ENUM_DESC_CONFIG, (uint8_t *)cfg->descriptors->config, 0
);
enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0);
enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1);
enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2);
enum_register_descriptor(ENUM_DESC_STRING, serial_id_desc, 3);
//enum_register_descriptor(ENUM_DESC_STRING, cdcacm_func_desc, 4);
//enum_register_descriptor(ENUM_DESC_STRING, msc_func_desc, 5);
/* Handle configuration */
enum_register_callback(ENUM_SETCONFIG, cb_usb_setconfig, NULL);
#ifdef USE_REMOTE_WAKE_ENABLE
/* Handle feature set/clear */
enum_register_callback(ENUM_SETFEATURE, cb_usb_setfeature, NULL);
enum_register_callback(ENUM_CLRFEATURE, cb_usb_clrfeature, NULL);
#endif
/* Register callbacks */
usb_event_enable(MAXUSB_EVENT_NOVBUS, cb_usb_event, NULL);
usb_event_enable(MAXUSB_EVENT_VBUS, cb_usb_event, NULL);
/* Start with USB in low power mode */
usb_app_sleep();
/* TODO: Fix priority */
NVIC_SetPriority(USB_IRQn, 6);
NVIC_EnableIRQ(USB_IRQn);
return 0;
}
static int cb_usb_setconfig(usb_setup_pkt *sud, void *cbdata)
{
LOG_DEBUG("usb", "setconfig %d", sud->wValue);
if (sud->wValue == s_device.descriptors->config->bConfigurationValue) {
return device_configure();
} else if (sud->wValue == 0) {
device_deconfigure();
return 0;
}
return -1;
}
#ifdef USE_REMOTE_WAKE_ENABLE
static int cb_usb_setfeature(usb_setup_pkt *sud, void *cbdata)
{
if (sud->wValue == FEAT_REMOTE_WAKE) {
remote_wake_en = 1;
return 0;
}
return -1;
}
static int cb_usb_clrfeature(usb_setup_pkt *sud, void *cbdata)
{
if (sud->wValue == FEAT_REMOTE_WAKE) {
remote_wake_en = 0;
return 0;
}
return -1;
}
#endif
/******************************************************************************/
static void usb_app_sleep(void)
{
/* TODO: Place low-power code here */
suspended = 1;
}
/******************************************************************************/
static void usb_app_wakeup(void)
{
/* TODO: Place low-power code here */
suspended = 0;
}
static void cb_timerReset(TimerHandle_t t)
{
(void)t;
LOG_DEBUG("usb", "cb_timerReset %08x", usb_get_status());
LOG_DEBUG("usb", "SYS_USBHS_Shutdown");
SYS_USBHS_Shutdown();
LOG_DEBUG("usb", "SYS_Reset_Periph");
SYS_Reset_Periph(SYS_RESET_USB);
//copy-paste from esb_init(), need to refactor
maxusb_cfg_options_t usb_opts;
usb_opts.enable_hs = 0;
usb_opts.delay_us = delay_us;
usb_opts.init_callback = cb_usb_init;
usb_opts.shutdown_callback = cb_usb_shutdown;
/* Initialize the usb module */
if (usb_init(&usb_opts) != 0) {
LOG_ERR("usb", "usb_init() failed");
return;
}
usb_event_enable(MAXUSB_EVENT_NOVBUS, cb_usb_event, NULL);
usb_event_enable(MAXUSB_EVENT_VBUS, cb_usb_event, NULL);
}
static void scheduleReset()
{
LOG_DEBUG("usb", "scheduleReset");
xTimerChangePeriodFromISR(timerWakeup, pdMS_TO_TICKS(500), NULL);
}
#if LOG_ENABLE_DEBUG
static const char *maxusb_event_string(maxusb_event_t evt)
{
static const char *names[MAXUSB_NUM_EVENTS] = {
[MAXUSB_EVENT_DPACT] = "DPACT",
[MAXUSB_EVENT_RWUDN] = "RWUDN",
[MAXUSB_EVENT_BACT] = "BACT",
[MAXUSB_EVENT_BRST] = "BRST",
[MAXUSB_EVENT_SUSP] = "SUSP",
[MAXUSB_EVENT_NOVBUS] = "NOVBUS",
[MAXUSB_EVENT_VBUS] = "VBUS",
[MAXUSB_EVENT_BRSTDN] = "BRSTDN",
[MAXUSB_EVENT_SUDAV] = "SUDAV",
};
return evt < MAXUSB_NUM_EVENTS ? names[evt] : "<INVALID>";
}
#endif
/******************************************************************************/
static int cb_usb_event(maxusb_event_t evt, void *data)
{
static int s_VBUS_SUSP_sequence = 0;
LOG_DEBUG("usb", "event %s (%d)\n", maxusb_event_string(evt), evt);
switch (evt) {
case MAXUSB_EVENT_NOVBUS:
usb_event_disable(MAXUSB_EVENT_BRST);
usb_event_disable(MAXUSB_EVENT_SUSP);
usb_event_disable(MAXUSB_EVENT_DPACT);
usb_disconnect();
device_deconfigure();
usb_app_sleep();
break;
case MAXUSB_EVENT_VBUS:
s_VBUS_SUSP_sequence = 1;
usb_event_clear(MAXUSB_EVENT_BRST);
usb_event_enable(MAXUSB_EVENT_BRST, cb_usb_event, NULL);
usb_event_clear(MAXUSB_EVENT_SUSP);
usb_event_enable(MAXUSB_EVENT_SUSP, cb_usb_event, NULL);
connect();
usb_app_sleep();
break;
case MAXUSB_EVENT_BRST:
usb_app_wakeup();
device_deconfigure();
suspended = 0;
break;
case MAXUSB_EVENT_SUSP:
if (!s_VBUS_SUSP_sequence) {
scheduleReset();
}
s_VBUS_SUSP_sequence = 0;
//usb_app_sleep();
break;
case MAXUSB_EVENT_DPACT:
usb_app_wakeup();
break;
default:
break;
}
return 0;
}
#ifndef EPICARDIUM_USB_EPC_USB_H_INCLUDED
#define EPICARDIUM_USB_EPC_USB_H_INCLUDED
/**
*
* EPC - it's not just a Universal Serial Bus,
* it's an Epic Serial Bus!
*/
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
struct esb_config;
typedef int (*esb_cfg_handler)(struct esb_config* self);
struct esb_config {
const char* name;
esb_cfg_handler init;
esb_cfg_handler configure;
esb_cfg_handler deconfigure;
esb_cfg_handler deinit;
struct esb_device_descriptors* descriptors;
void* deviceData;
};
int esb_init(struct esb_config* cfg);
void esb_deinit(void);
#endif//EPICARDIUM_USB_EPC_USB_H_INCLUDED
/**
* backend-independent implementation of the mass storage device
* services MSC API and esb_cfg API
*/
#include "usb/mass_storage.h"
#include "usb/epc_usb.h"
#include "usb/descriptors.h"
#include "os/core.h"
#include "msc.h"
#include "usb.h"
#include "max32665.h"
#define VENDOR_STRING "CCC"
#define PRODUCT_STRING "card10"
#define VERSION_STRING "1.0"
static inline struct config_descriptor_msc *descriptors(struct esb_config *self)
{
return self->descriptors->msc;
}
int esb_msc_configure(struct esb_config *self)
{
LOG_DEBUG("msc", "configure");
struct config_descriptor_msc *dsc = descriptors(self);
const msc_cfg_t msc_cfg = {
dsc->endpoint_out.bEndpointAddress,
MXC_USBHS_MAX_PACKET, /* OUT max packet size */
dsc->endpoint_in.bEndpointAddress & 0x0f,
MXC_USBHS_MAX_PACKET, /* IN max packet size */
};
return msc_configure(&msc_cfg);
}
int esb_msc_deconfigure(struct esb_config *self)
{
LOG_DEBUG("msc", "deconfigure");
(void)self;
return msc_deconfigure();
}
int esb_msc_init(struct esb_config *self)
{
LOG_DEBUG("msc", "init");
static const msc_idstrings_t ids = { VENDOR_STRING,
PRODUCT_STRING,
VERSION_STRING };
msc_mem_t *mem = self->deviceData;
struct config_descriptor_msc *dsc = descriptors(self);
return msc_init(&dsc->msc_interface, &ids, mem);
}
#ifndef EPICARDIUM_USB_MSC_H_INCLUDED
#define EPICARDIUM_USB_MSC_H_INCLUDED
#include "usb/epc_usb.h"
int esb_msc_configure(struct esb_config* self);
int esb_msc_deconfigure(struct esb_config* self);
int esb_msc_init(struct esb_config* self);
#endif//EPICARDIUM_USB_MSC_H_INCLUDED
#include "os/core.h"
#include "os/mutex.h"
#include "api/dispatcher.h"
#include "FreeRTOS.h"
#include "task.h"
#define TIMEOUT pdMS_TO_TICKS(2000)
TaskHandle_t dispatcher_task_id;
struct mutex api_mutex = { 0 };
void dispatcher_mutex_init(void)
{
mutex_create(&api_mutex);
}
/*
* API dispatcher task. This task will sleep until an API call is issued and
* then wake up to dispatch it.
*/
void vApiDispatcher(void *pvParameters)
{
LOG_DEBUG("dispatcher", "Ready.");
while (1) {
if (api_dispatcher_poll()) {
mutex_lock(&api_mutex);
api_dispatcher_exec();
mutex_unlock(&api_mutex);
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
}
#include "os/mutex.h"
#include "os/core.h"
#include "epicardium.h"
#include "api/interrupt-sender.h"
#include "user_core/user_core.h"
#include <assert.h>
struct interrupt_priv {
/* Whether this interrupt can be triggered */
bool int_enabled[EPIC_INT_NUM];
/* Whether this interrupt is waiting to be delivered */
bool int_pending[EPIC_INT_NUM];
/* Whether any interrupts are currently waiting to be triggered */
bool has_pending;
};
static struct interrupt_priv interrupt_data;
static struct mutex interrupt_mutex;
static TaskHandle_t interrupts_task;
void interrupt_trigger(api_int_id_t id)
{
assert(id < EPIC_INT_NUM);
mutex_lock(&interrupt_mutex);
if (interrupt_data.int_enabled[id]) {
interrupt_data.int_pending[id] = true;
interrupt_data.has_pending = true;
mutex_unlock(&interrupt_mutex);
xTaskNotifyGive(interrupts_task);
} else {
mutex_unlock(&interrupt_mutex);
}
}
void interrupt_trigger_sync(api_int_id_t id)
{
assert(id < EPIC_INT_NUM);
mutex_lock(&interrupt_mutex);
if (!interrupt_data.int_enabled[id])
goto out;
while (!api_interrupt_is_ready())
;
api_interrupt_trigger(id);
/* Break the dispatcher task out of a potential call
* to epic_sleep() */
xTaskNotifyGive(dispatcher_task_id);
out:
mutex_unlock(&interrupt_mutex);
}
/*
* This function solely exists because of that one use of interrupts that breaks
* the rules: The RTC ALARM interrupt is triggered from a hardware ISR where
* interrupt_trigger_sync() won't work because it needs to lock a mutex.
*
* DO NOT USE THIS FUNCTION IN ANY NEW CODE.
*/
void __attribute__((deprecated)) interrupt_trigger_unsafe(api_int_id_t id)
{
assert(id < EPIC_INT_NUM);
if (!interrupt_data.int_enabled[id])
return;
while (!api_interrupt_is_ready())
;
api_interrupt_trigger(id);
}
static void interrupt_set_enabled(api_int_id_t id, bool enabled)
{
assert(id < EPIC_INT_NUM);
mutex_lock(&interrupt_mutex);
interrupt_data.int_enabled[id] = enabled;
mutex_unlock(&interrupt_mutex);
}
static bool interrupt_get_enabled(api_int_id_t id)
{
assert(id < EPIC_INT_NUM);
bool enabled;
mutex_lock(&interrupt_mutex);
enabled = interrupt_data.int_enabled[id];
mutex_unlock(&interrupt_mutex);
return enabled;
}
void interrupt_init(void)
{
if (interrupt_mutex.name == NULL)
mutex_create(&interrupt_mutex);
api_interrupt_init();
/* Reset all irqs to disabled */
for (size_t i = 0; i < EPIC_INT_NUM; i++) {
interrupt_set_enabled(i, false);
}
/* Reset interrupt is always enabled */
interrupt_set_enabled(EPIC_INT_RESET, true);
}
/* Epic-calls {{{ */
int epic_interrupt_enable(api_int_id_t int_id)
{
if (int_id >= EPIC_INT_NUM) {
return -EINVAL;
}
interrupt_set_enabled(int_id, true);
return 0;
}
int epic_interrupt_disable(api_int_id_t int_id)
{
if (int_id >= EPIC_INT_NUM || int_id == EPIC_INT_RESET) {
return -EINVAL;
}
interrupt_set_enabled(int_id, false);
return 0;
}
int epic_interrupt_is_enabled(api_int_id_t int_id, bool *enabled)
{
if (int_id >= EPIC_INT_NUM) {
return -EINVAL;
}
*enabled = interrupt_get_enabled(int_id);
return 0;
}
/* }}} */
void vInterruptsTask(void *pvParameters)
{
interrupts_task = xTaskGetCurrentTaskHandle();
while (true) {
mutex_lock(&interrupt_mutex);
if (!interrupt_data.has_pending) {
/* Wait for a wakeup event from interrupt_trigger() */
mutex_unlock(&interrupt_mutex);
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
mutex_lock(&interrupt_mutex);
}
while (!api_interrupt_is_ready()) {
mutex_unlock(&interrupt_mutex);
vTaskDelay(pdMS_TO_TICKS(5));
mutex_lock(&interrupt_mutex);
}
api_int_id_t current_irq = EPIC_INT_NUM;
for (size_t i = 0; i < EPIC_INT_NUM; i++) {
if (interrupt_data.int_pending[i]) {
current_irq = i;
interrupt_data.int_pending[i] = false;
break;
}
}
if (current_irq == EPIC_INT_NUM) {
interrupt_data.has_pending = false;
} else if (interrupt_data.int_enabled[current_irq]) {
api_interrupt_trigger(current_irq);
/* Break the dispatcher task out of a potential call
* to epic_sleep() */
xTaskNotifyGive(dispatcher_task_id);
}
mutex_unlock(&interrupt_mutex);
}
}
#pragma once
#include "epicardium.h"
/* ---------- Interrupts --------------------------------------------------- */
void interrupt_init(void);
void interrupt_trigger(api_int_id_t id);
void interrupt_trigger_sync(api_int_id_t id);
void interrupt_trigger_unsafe(api_int_id_t id) __attribute__((deprecated(
"interrupt_trigger_unsafe() is racy and only exists for legacy code."
)));
void vInterruptsTask(void *pvParameters);
#include "epicardium.h" #include "epicardium.h"
#include "modules/log.h" #include "os/core.h"
#include "modules/modules.h" #include "modules/modules.h"
#include "os/config.h"
#include "os/mutex.h"
#include "user_core/user_core.h"
#include "api/dispatcher.h" #include "api/dispatcher.h"
#include "api/interrupt-sender.h"
#include "l0der/l0der.h" #include "l0der/l0der.h"
#include "card10.h" #include "card10.h"
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"
#include "semphr.h"
#include <assert.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdbool.h> #include <stdbool.h>
#define PYCARDIUM_IVT (void *)0x10080000 #define PYCARDIUM_IVT (void *)0x100a0000
#define BLOCK_WAIT pdMS_TO_TICKS(1000) #define BLOCK_WAIT pdMS_TO_TICKS(1000)
/* /*
* Loading an empty filename into Pycardium will drop straight into the * Loading an empty filename into Pycardium will drop straight into the
...@@ -25,8 +27,7 @@ ...@@ -25,8 +27,7 @@
#define PYINTERPRETER "" #define PYINTERPRETER ""
static TaskHandle_t lifecycle_task = NULL; static TaskHandle_t lifecycle_task = NULL;
static StaticSemaphore_t core1_mutex_data; static struct mutex core1_mutex = { 0 };
static SemaphoreHandle_t core1_mutex = NULL;
enum payload_type { enum payload_type {
PL_INVALID = 0, PL_INVALID = 0,
...@@ -49,6 +50,7 @@ static volatile struct load_info async_load = { ...@@ -49,6 +50,7 @@ static volatile struct load_info async_load = {
/* Whether to write the menu script before attempting to load. */ /* Whether to write the menu script before attempting to load. */
static volatile bool write_menu = false; static volatile bool write_menu = false;
static bool execute_elfs = false;
/* Helpers {{{ */ /* Helpers {{{ */
...@@ -91,17 +93,27 @@ static int do_load(struct load_info *info) ...@@ -91,17 +93,27 @@ static int do_load(struct load_info *info)
struct l0dable_info l0dable; struct l0dable_info l0dable;
int res; int res;
/* Callers of do_load() must first lock the core1_mutex. */
mutex_assert_locked(&core1_mutex);
if (*info->name == '\0') { if (*info->name == '\0') {
LOG_INFO("lifecycle", "Loading Python interpreter ..."); LOG_INFO("lifecycle", "Loading Python interpreter ...");
} else { } else {
LOG_INFO("lifecycle", "Loading \"%s\" ...", info->name); LOG_INFO("lifecycle", "Loading \"%s\" ...", info->name);
} }
if (xSemaphoreTake(api_mutex, BLOCK_WAIT) != pdTRUE) { if (info->type == PL_L0DABLE && !execute_elfs) {
LOG_ERR("lifecycle", "API blocked"); LOG_WARN(
return -EBUSY; "lifecycle", "Execution of .elf l0dables is disabled."
);
return -EPERM;
} }
/* Signal the dispatcher to return early from applicable API calls. */
xTaskNotifyGive(dispatcher_task_id);
mutex_lock(&api_mutex);
if (info->do_reset) { if (info->do_reset) {
LOG_DEBUG("lifecycle", "Triggering core 1 reset."); LOG_DEBUG("lifecycle", "Triggering core 1 reset.");
...@@ -110,15 +122,33 @@ static int do_load(struct load_info *info) ...@@ -110,15 +122,33 @@ static int do_load(struct load_info *info)
/* /*
* Wait for the core to become ready to accept a new payload. * Wait for the core to become ready to accept a new payload.
*
* If it is not yet ready, hand back control of the API mutex to the
* dispatcher so it can finish dispatching a current API call. This is
* necessary for payloads which have interrupts disabled during an API
* call.
*/ */
core1_wait_ready(); while (!core1_is_ready()) {
/*
* Wake up the dispatcher task prematurely. This is needed so
* the second xTaskNotifyGive() below can then break out the
* dispatcher from e.g. an epic_sleep() call.
*/
xTaskNotifyGive(dispatcher_task_id);
mutex_unlock(&api_mutex);
/* Sleep so the dispatcher task can take the lock. */
vTaskDelay(8);
/* Signal the dispatcher to return early from applicable API calls. */
xTaskNotifyGive(dispatcher_task_id);
mutex_lock(&api_mutex);
}
/* /*
* Reinitialize Hardware & Drivers * Reinitialize Hardware & Drivers
*/ */
res = hardware_reset(); res = hardware_reset();
if (res < 0) { if (res < 0) {
return res; goto out_free_api;
} }
switch (info->type) { switch (info->type) {
...@@ -128,25 +158,29 @@ static int do_load(struct load_info *info) ...@@ -128,25 +158,29 @@ static int do_load(struct load_info *info)
core1_load(PYCARDIUM_IVT, info->name); core1_load(PYCARDIUM_IVT, info->name);
break; break;
case PL_L0DABLE: case PL_L0DABLE:
assert(execute_elfs);
res = l0der_load_path(info->name, &l0dable); res = l0der_load_path(info->name, &l0dable);
if (res != 0) { if (res != 0) {
LOG_ERR("lifecycle", "l0der failed: %d\n", res); LOG_ERR("lifecycle", "l0der failed: %d\n", res);
xSemaphoreGive(api_mutex); res = -ENOEXEC;
return -ENOEXEC; goto out_free_api;
} }
core1_load(l0dable.isr_vector, "");
core1_load(l0dable.isr_vector, "");
break; break;
default: default:
LOG_ERR("lifecyle", LOG_ERR("lifecyle",
"Attempted to load invalid payload (%s)", "Attempted to load invalid payload (%s)",
info->name); info->name);
xSemaphoreGive(api_mutex); res = -EINVAL;
return -EINVAL; goto out_free_api;
} }
xSemaphoreGive(api_mutex); res = 0;
return 0; out_free_api:
mutex_unlock(&api_mutex);
return res;
} }
/* /*
...@@ -154,6 +188,9 @@ static int do_load(struct load_info *info) ...@@ -154,6 +188,9 @@ static int do_load(struct load_info *info)
*/ */
static int load_sync(char *name, bool reset) static int load_sync(char *name, bool reset)
{ {
/* Callers of load_sync() must first lock the core1_mutex. */
mutex_assert_locked(&core1_mutex);
int ret = load_stat(name); int ret = load_stat(name);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
...@@ -176,6 +213,9 @@ static int load_sync(char *name, bool reset) ...@@ -176,6 +213,9 @@ static int load_sync(char *name, bool reset)
*/ */
static int load_async(char *name, bool reset) static int load_async(char *name, bool reset)
{ {
/* Callers of load_async() must first lock the core1_mutex. */
mutex_assert_locked(&core1_mutex);
int ret = load_stat(name); int ret = load_stat(name);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
...@@ -246,11 +286,7 @@ static void load_menu(bool reset) ...@@ -246,11 +286,7 @@ static void load_menu(bool reset)
{ {
LOG_DEBUG("lifecycle", "Into the menu"); LOG_DEBUG("lifecycle", "Into the menu");
if (xSemaphoreTake(core1_mutex, BLOCK_WAIT) != pdTRUE) { mutex_lock(&core1_mutex);
LOG_ERR("lifecycle",
"Can't load because mutex is blocked (menu).");
return;
}
int ret = load_async("menu.py", reset); int ret = load_async("menu.py", reset);
if (ret < 0) { if (ret < 0) {
...@@ -270,7 +306,7 @@ static void load_menu(bool reset) ...@@ -270,7 +306,7 @@ static void load_menu(bool reset)
} }
} }
xSemaphoreGive(core1_mutex); mutex_unlock(&core1_mutex);
} }
/* Helpers }}} */ /* Helpers }}} */
...@@ -290,14 +326,9 @@ void epic_system_reset(void) ...@@ -290,14 +326,9 @@ void epic_system_reset(void)
*/ */
int epic_exec(char *name) int epic_exec(char *name)
{ {
if (xSemaphoreTake(core1_mutex, BLOCK_WAIT) != pdTRUE) { mutex_lock(&core1_mutex);
LOG_ERR("lifecycle",
"Can't load because mutex is blocked (epi exec).");
return -EBUSY;
}
int ret = load_sync(name, true); int ret = load_sync(name, true);
xSemaphoreGive(core1_mutex); mutex_unlock(&core1_mutex);
return ret; return ret;
} }
...@@ -310,13 +341,9 @@ int epic_exec(char *name) ...@@ -310,13 +341,9 @@ int epic_exec(char *name)
*/ */
int __epic_exec(char *name) int __epic_exec(char *name)
{ {
if (xSemaphoreTake(core1_mutex, BLOCK_WAIT) != pdTRUE) { mutex_lock(&core1_mutex);
LOG_ERR("lifecycle",
"Can't load because mutex is blocked (1 exec).");
return -EBUSY;
}
int ret = load_async(name, false); int ret = load_async(name, false);
xSemaphoreGive(core1_mutex); mutex_unlock(&core1_mutex);
return ret; return ret;
} }
...@@ -353,37 +380,37 @@ void return_to_menu(void) ...@@ -353,37 +380,37 @@ void return_to_menu(void)
void vLifecycleTask(void *pvParameters) void vLifecycleTask(void *pvParameters)
{ {
lifecycle_task = xTaskGetCurrentTaskHandle(); lifecycle_task = xTaskGetCurrentTaskHandle();
core1_mutex = xSemaphoreCreateMutexStatic(&core1_mutex_data); mutex_create(&core1_mutex);
mutex_lock(&core1_mutex);
if (xSemaphoreTake(core1_mutex, 0) != pdTRUE) {
LOG_CRIT(
"lifecycle", "Failed to acquire mutex after creation."
);
vTaskDelay(portMAX_DELAY);
}
LOG_DEBUG("lifecycle", "Booting core 1 ..."); LOG_DEBUG("lifecycle", "Booting core 1 ...");
core1_boot(); core1_boot();
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(10));
xSemaphoreGive(core1_mutex); mutex_unlock(&core1_mutex);
/* If `main.py` exists, start it. Otherwise, start `menu.py`. */ /*
if (epic_exec("main.py") < 0) { * If `main.py` exists, start it. Otherwise, start `menu.py`.
return_to_menu(); *
* We are not using epic_exec() & return_to_menu() here because those
* trigger a reset which is undesirable during startup.
*/
mutex_lock(&core1_mutex);
int ret = load_sync("main.py", false);
mutex_unlock(&core1_mutex);
if (ret < 0) {
load_menu(false);
} }
hardware_init(); hardware_init();
execute_elfs = config_get_boolean_with_default("execute_elf", false);
/* When triggered, reset core 1 to menu */ /* When triggered, reset core 1 to menu */
while (1) { while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (xSemaphoreTake(core1_mutex, BLOCK_WAIT) != pdTRUE) { mutex_lock(&core1_mutex);
LOG_ERR("lifecycle",
"Can't load because mutex is blocked (task).");
continue;
}
if (write_menu) { if (write_menu) {
write_menu = false; write_menu = false;
...@@ -397,8 +424,13 @@ void vLifecycleTask(void *pvParameters) ...@@ -397,8 +424,13 @@ void vLifecycleTask(void *pvParameters)
} }
} }
do_load((struct load_info *)&async_load); ret = do_load((struct load_info *)&async_load);
mutex_unlock(&core1_mutex);
xSemaphoreGive(core1_mutex); if (ret < 0) {
LOG_ERR("lifecycle", "Error loading payload: %d", ret);
return_to_menu();
}
} }
} }
user_core_sources = files(
'dispatcher.c',
'interrupts.c',
'lifecycle.c',
'migration.c',
)
#include "epicardium.h"
#include "os/core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void migration_delete_app_launchers(void)
{
int fd = epic_file_opendir("/");
struct epic_stat entry;
for (;;) {
epic_file_readdir(fd, &entry);
if (entry.type == EPICSTAT_NONE) {
// End
break;
}
const char *dot = strrchr(entry.name, '.');
if (dot && !strcmp(dot, ".py")) {
const char launcher[] = "# Launcher script for ";
char launcher_buf[strlen(launcher)];
int fd = epic_file_open(entry.name, "r");
if (fd >= 0) {
int n = epic_file_read(
fd, launcher_buf, sizeof(launcher_buf)
);
epic_file_close(fd);
if (n == (int)sizeof(launcher_buf) &&
!memcmp(launcher,
launcher_buf,
sizeof(launcher_buf))) {
LOG_INFO(
"migration",
"Delete old launcher %s",
entry.name
);
epic_file_unlink(entry.name);
}
}
}
}
epic_file_close(fd);
}