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
  • add_menu_vibration
  • blinkisync-as-preload
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • ch3/splashscreen
  • dualcore
  • dx/flatten-config-module
  • dx/meh-bdf-to-stm
  • freertos-btle
  • genofire/ble-follow-py
  • koalo/bhi160-works-but-dirty
  • koalo/factory-reset
  • koalo/wip/i2c-for-python
  • master
  • msgctl/faultscreen
  • msgctl/textbuffer_api
  • plaetzchen/ios-workaround
  • rahix/bhi
  • rahix/bluetooth-app-favorite
  • rahix/bma
  • rahix/user-space-ctx
  • renze/hatchery_apps
  • renze/safe_mode
  • schleicher-test
  • schneider/212-reset-hardware-when-entering-repl
  • schneider/ancs
  • schneider/ble-buffers
  • schneider/ble-central
  • schneider/ble-ecg-stream-visu
  • schneider/ble-fixes-2020-3
  • schneider/ble-mini-demo
  • schneider/ble-stability
  • schneider/ble-stability-new-phy
  • schneider/bonding
  • schneider/bonding-fail-if-full
  • schneider/bootloader-update-9a0d158
  • schneider/deepsleep
  • schneider/deepsleep2
  • schneider/deepsleep4
  • schneider/default-main
  • schneider/freertos-list-debug
  • schneider/fundamental-test
  • schneider/iaq-python
  • schneider/ir
  • schneider/max30001
  • schneider/max30001-epicaridum
  • schneider/max30001-pycardium
  • schneider/maxim-sdk-update
  • schneider/mp-exception-print
  • schneider/mp-for-old-bl
  • schneider/png
  • schneider/schleicher-test
  • schneider/sdk-0.2.1-11
  • schneider/sdk-0.2.1-7
  • schneider/sleep-display
  • schneider/spo2-playground
  • schneider/stream-locks
  • schneider/v1.17-changelog
  • bootloader-v1
  • release-1
  • v0.0
  • v1.0
  • v1.1
  • v1.10
  • v1.11
  • v1.12
  • v1.13
  • v1.14
  • v1.15
  • v1.16
  • v1.17
  • v1.18
  • v1.2
  • v1.3
  • v1.4
  • v1.5
  • v1.6
  • v1.7
  • v1.8
  • v1.9
82 results

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
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • color-2
  • color-html
  • color-html-2
  • color-html-3
  • dualcore
  • freertos-btle
  • master
  • micro-modules
  • patch-1
  • patch-2
  • patch-3
  • rahix/bma
  • schneider/mp-for-old-bl
  • wink/trng
18 results
Show changes
Showing
with 1551 additions and 491 deletions
/*
* support routines for FatFs
*/
#include <stddef.h> //NULL
#include <stdio.h>
#include <stdbool.h>
#include <ff.h>
#include <FreeRTOS.h>
#include <semphr.h>
static const TCHAR *rcstrings =
_T("OK\0DISK_ERR\0INT_ERR\0NOT_READY\0NO_FILE\0NO_PATH\0INVALID_NAME\0")
_T("DENIED\0EXIST\0INVALID_OBJECT\0WRITE_PROTECTED\0INVALID_DRIVE\0")
_T("NOT_ENABLED\0NO_FILESYSTEM\0MKFS_ABORTED\0TIMEOUT\0LOCKED\0")
_T("NOT_ENOUGH_CORE\0TOO_MANY_OPEN_FILES\0INVALID_PARAMETER\0");
static bool mount(void);
DIR dir;
FATFS FatFs;
static volatile struct {
bool initiaized;
} s_state = {
.initiaized = false,
};
void fatfs_init()
{
if (mount()) {
s_state.initiaized = true;
printf("FatFs mounted\n");
}
}
const char *f_get_rc_string(FRESULT rc)
{
FRESULT i;
const char *p = rcstrings;
for (i = 0; i != rc && *p; i++) {
while (*p++)
;
}
return p;
}
static bool mount()
{
FRESULT res;
res = f_mount(&FatFs, "/", 0);
if (res != FR_OK) {
printf("f_mount error %s\n", f_get_rc_string(res));
return false;
}
res = f_opendir(&dir, "0:");
if (res != FR_OK) {
printf("f_opendir error %s\n", f_get_rc_string(res));
return false;
}
return true;
}
/*------------------------------------------------------------------------*/
/* Create a Synchronization Object */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount() function to create a new
/ synchronization object for the volume, such as semaphore and mutex.
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
*/
/*
* Return value:
* - 1: Function succeeded
* - 0: Could not create the sync object
*/
int ff_cre_syncobj(BYTE vol, FF_SYNC_t *sobj)
{
*sobj = xSemaphoreCreateMutex();
return (int)(*sobj != NULL);
}
/*------------------------------------------------------------------------*/
/* Delete a Synchronization Object */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount() function to delete a synchronization
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
/ the f_mount() function fails with FR_INT_ERR.
*/
/*
* Return value:
* - 1: Function succeeded
* - 0: Could not delete due to an error
*/
int ff_del_syncobj(FF_SYNC_t sobj)
{
printf("%s\n", __PRETTY_FUNCTION__);
/* FreeRTOS */
vSemaphoreDelete(sobj);
return 1;
}
/*------------------------------------------------------------------------*/
/* Request Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on entering file functions to lock the volume.
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
*/
/*
* Return value:
* - 1: Got a grant to access the volume
* - 0: Could not get a grant
*/
int ff_req_grant(FF_SYNC_t sobj)
{
/* FreeRTOS */
return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
}
/*------------------------------------------------------------------------*/
/* Release Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on leaving file functions to unlock the volume.
*/
void ff_rel_grant(FF_SYNC_t sobj)
{
/* FreeRTOS */
xSemaphoreGive(sobj);
}
#include "epicardium.h"
#include "api/dispatcher.h"
#include "usb/epc_usb.h"
#include "fs/filesystem.h"
#include "os/core.h"
#include "modules/modules.h"
#include "modules/stream.h"
#include "drivers/drivers.h"
#include "user_core/interrupts.h"
#include "user_core/user_core.h"
#include "card10.h"
#include "leds.h"
#include "pb.h"
#include "pmic.h"
#include "portexpander.h"
#include "max86150.h"
#include "ble/ble_api.h"
#include "gpio.h"
#include "i2c.h"
#include "rtc.h"
#include "spi.h"
#include "wdt.h"
/*
* Early init is called at the very beginning and is meant for modules which
* absolutely need to start as soon as possible. hardware_early_init() blocks
* which means code in here should be fast.
*/
int hardware_early_init(void)
{
/*
* Watchdog timer
*/
watchdog_init();
/*
* I2C bus for onboard peripherals (ie. PMIC, BMA400, BHI160, BME680,
* ...)
*/
I2C_Shutdown(MXC_I2C1_BUS0);
I2C_Init(MXC_I2C1_BUS0, I2C_FAST_MODE, NULL);
#ifndef CARD10_DEBUG_CORE1
/*
* SAO I2C bus
*/
I2C_Shutdown(MXC_I2C0_BUS0);
I2C_Init(MXC_I2C0_BUS0, I2C_FAST_MODE, NULL);
#endif
/*
* GPIO peripheral.
*/
GPIO_Init();
/* Set the power hold pin, so the PMIC does not turn off again */
const gpio_cfg_t pwr_hold_pin = {
PORT_0, PIN_30, GPIO_FUNC_OUT, GPIO_PAD_NONE
};
GPIO_Config(&pwr_hold_pin);
GPIO_OutSet(&pwr_hold_pin);
/*
* PMIC (MAX77650)
*/
pmic_init();
pmic_set_led(0, 0);
pmic_set_led(1, 0);
pmic_set_led(2, 0);
/*
* Harmonic Board Portexpander
*/
portexpander_init();
/*
* Buttons
*/
PB_Init();
/* Enable 32 kHz output */
while (RTC_SquareWave(MXC_RTC, SQUARE_WAVE_ENABLED, F_32KHZ, NULL) ==
E_BUSY)
;
/*
* RNG
*/
rng_init();
/* If we don't have a valid time yet, set it to 2019-01-01 */
if (RTC_GetSecond() < 1546300800L) {
epic_rtc_set_milliseconds(1546300800UL * 1000);
}
/*
* SPI for ECG
*/
const sys_cfg_spi_t spi17y_master_cfg = { .map = MAP_A,
.ss0 = Enable,
.ss1 = Disable,
.ss2 = Disable,
.num_io = 2 };
if (SPI_Init(SPI0, 0, SPI_SPEED, spi17y_master_cfg) != 0) {
LOG_ERR("init", "Error configuring SPI");
while (1)
;
}
/*
* Display
*/
disp_init();
/*
* RGB LEDs
*/
leds_init();
#ifdef CARD10_DEBUG_CORE1
/*
* The SAO pins can be reconfigured for SWCLK2 and SWDIO2 which allows
* debugging core 1. This feature can optionally be enabled at
* compile-time.
*/
LOG_WARN("init", "Core 1 Debugger Mode");
static const gpio_cfg_t swclk = {
PORT_0,
PIN_7,
GPIO_FUNC_ALT3,
GPIO_PAD_NONE,
};
static const gpio_cfg_t swdio = {
PORT_0,
PIN_6,
GPIO_FUNC_ALT3,
GPIO_PAD_NONE,
};
GPIO_Config(&swclk);
GPIO_Config(&swdio);
#endif /* CARD10_DEBUG_CORE1 */
/*
* Enable SEV-ON-PEND which is needed for proper working of the FreeRTOS
* tickless idle sleep in Epicardium.
*/
SCB->SCR |= SCB_SCR_SEVONPEND_Msk;
/*
* USB-Serial
*/
if (epic_usb_cdcacm() < 0) {
LOG_ERR("startup", "USB-Serial unavailable");
}
/*
* Flash & FatFS
*/
fatfs_init();
/*
* API Dispatcher & API Interrupts
*/
interrupt_init();
api_dispatcher_init();
/*
* Sensor streams
*/
stream_init();
/*
* Hardware/Peripheral Locks
*/
hwlock_init();
/*
* API Dispatcher Mutex
*/
dispatcher_mutex_init();
/*
* MAX30001 mutex init
*/
max30001_mutex_init();
/*
* max86150 mutex init
*/
max86150_mutex_init();
max86150_shut_down();
/*
* BME680 Sensor
*/
epic_bme680_init();
/* Allow user space to trigger interrupts.
* Used for BLE, not sure if needed. */
SCB->CCR |= SCB_CCR_USERSETMPEND_Msk;
return 0;
}
/*
* hardware_init() is called after the core has been bootstrapped and is meant
* for less critical initialization steps. Modules which initialize here should
* be robust against a l0dable using their API before initialization is done.
*
* Ideally, acquire a lock in hardware_early_init() and release it in
* hardware_init() once initialization is done.
*/
int hardware_init(void)
{
/* Watchdog clearer software timer */
watchdog_clearer_init();
/* Light Sensor */
LOG_DEBUG("init", "Starting light sensor ...");
epic_light_sensor_run();
return 0;
}
/*
* hardware_reset() is called whenever a new l0dable is started. hardware_reset()
* should bring all peripherals back into a known initial state. This does not
* necessarily mean resetting the peripheral entirely but hardware_reset()
* should at least bring the API facing part of a peripheral back into the state
* a fresh booted l0dable expects.
*/
int hardware_reset(void)
{
/*
* API Dispatcher & API Interrupts
*/
interrupt_init();
api_dispatcher_init();
/*
* close all FDs currently owned by core1
*/
fatfs_close_all(EPICARDIUM_COREMASK_1);
/* Personal State */
const int personal_state_is_persistent =
epic_personal_state_is_persistent();
if (personal_state_is_persistent == 0) {
epic_personal_state_set(STATE_NONE, 0);
}
/*
* LEDs
*/
if (personal_state_is_persistent) {
epic_leds_clear_all(0, 0, 0);
} else {
leds_init();
}
epic_leds_set_rocket(0, 0);
epic_leds_set_rocket(1, 0);
epic_leds_set_rocket(2, 0);
/*
* Display
*/
disp_init();
epic_disp_backlight(20);
/*
* Vibration Motor
*/
epic_vibra_set(false);
/*
* BHI160
*/
epic_bhi160_disable_all_sensors();
epic_max30001_disable_sensor();
epic_max86150_disable_sensor();
/*
* BLE
*/
/* Reset advertisement data */
ble_adv_setup();
/* Start advertising again if needed */
epic_ble_set_mode(false, false);
return 0;
}
#include "os/core.h"
#include "modules/modules.h"
#include "os/mutex.h"
#include "FreeRTOS.h"
#include "task.h"
#include <errno.h>
static struct mutex hwlock_mutex[_HWLOCK_MAX] = { { 0 } };
void hwlock_init(void)
{
for (int i = 0; i < _HWLOCK_MAX; i++) {
/*
* TODO: mutex_create() names these all these mutexes
* "&hwlock_mutex[i]" which is not helpful at all. We
* should somehow rename them to the actual hwlock names.
*/
mutex_create(&hwlock_mutex[i]);
}
}
void hwlock_acquire(enum hwlock_periph p)
{
assert(p < _HWLOCK_MAX);
mutex_lock(&hwlock_mutex[p]);
}
int hwlock_acquire_nonblock(enum hwlock_periph p)
{
assert(p < _HWLOCK_MAX);
if (mutex_trylock(&hwlock_mutex[p])) {
return 0;
} else {
return -EBUSY;
}
}
void hwlock_release(enum hwlock_periph p)
{
assert(p < _HWLOCK_MAX);
mutex_unlock(&hwlock_mutex[p]);
}
#include "leds.h"
void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b)
{
leds_set(led, r, g, b);
leds_update();
}
\ No newline at end of file
#pragma once
#define SEV_DEBUG "D"
#define SEV_INFO "I"
#define SEV_WARN "W"
#define SEV_ERR "E"
#define SEV_CRIT "C"
/* Whether to enable logging at all */
#ifndef LOG_ENABLE
#define LOG_ENABLE 1
#endif
/* Whether to enable even more verbose logging */
#ifndef LOG_ENABLE_DEBUG
#define LOG_ENABLE_DEBUG 0
#endif
/* Whether to enable colorful log */
#ifndef LOG_ENABLE_COLOR
#define LOG_ENABLE_COLOR 1
#endif
#if LOG_ENABLE
int log_msg(const char *subsys, const char *format, ...)
__attribute__((format(printf, 2, 3)));
#if LOG_ENABLE_DEBUG
#define LOG_DEBUG(subsys, format, ...) \
log_msg(subsys, SEV_DEBUG format, ##__VA_ARGS__)
#else /* LOG_ENABLE_DEBUG */
#define LOG_DEBUG(subsys, format, ...)
#endif /* LOG_ENABLE_DEBUG */
#define LOG_INFO(subsys, format, ...) \
log_msg(subsys, SEV_INFO format, ##__VA_ARGS__)
#define LOG_WARN(subsys, format, ...) \
log_msg(subsys, SEV_WARN format, ##__VA_ARGS__)
#define LOG_ERR(subsys, format, ...) \
log_msg(subsys, SEV_ERR format, ##__VA_ARGS__)
#define LOG_CRIT(subsys, format, ...) \
log_msg(subsys, SEV_CRIT format, ##__VA_ARGS__)
#else /* LOG_ENABLE_DEBUG */
inline __attribute__((format(printf, 2, 3))) int
log_msg(const char *subsys, const char *format, ...)
{
return 0;
}
#define LOG_DEBUG(subsys, format, ...)
#define LOG_INFO(subsys, format, ...)
#define LOG_WARN(subsys, format, ...)
#define LOG_ERR(subsys, format, ...)
#define LOG_CRIT(subsys, format, ...)
#endif /* LOG_ENABLE_DEBUG */
module_sources = files(
'display.c',
'fatfs.c',
'leds.c',
'log.c',
'pmic.c',
'serial.c',
'hardware.c',
'hw-lock.c',
'personal_state.c',
'stream.c',
'vibra.c',
'light_sensor.c',
)
#ifndef MODULES_H
#define MODULES_H
/* FatFS */
void fatfs_init(void);
#include "FreeRTOS.h"
#include "gpio.h"
#include "os/mutex.h"
#include "epicardium.h"
/* ---------- Serial ------------------------------------------------------- */
#define SERIAL_READ_BUFFER_SIZE 128
void vSerialTask(void *pvParameters);
#include <stdint.h>
#include <stdbool.h>
/* ---------- PMIC --------------------------------------------------------- */
/* In 1/10s */
#define PMIC_PRESS_SLEEP 20
#define PMIC_PRESS_POWEROFF 40
void vPmicTask(void *pvParameters);
/* ---------- Hardware Init & Reset ---------------------------------------- */
int hardware_early_init(void);
int hardware_init(void);
int hardware_reset(void);
// Forces an unlock of the display. Only to be used in epicardium
void disp_forcelock();
/* ---------- LED Animation / Personal States ------------------------------ */
#define PERSONAL_STATE_LED 14
void vLedTask(void *pvParameters);
int personal_state_enabled();
/* ---------- BLE ---------------------------------------------------------- */
void vBleTask(void *pvParameters);
bool ble_is_enabled(void);
void ble_uart_write(uint8_t *pValue, uint8_t len);
/* ---------- Hardware (Peripheral) Locks ---------------------------------- */
void hwlock_init(void);
enum hwlock_periph {
HWLOCK_I2C = 0,
HWLOCK_ADC,
HWLOCK_LED,
HWLOCK_SPI_ECG,
_HWLOCK_MAX,
};
void hwlock_acquire(enum hwlock_periph p);
int hwlock_acquire_nonblock(enum hwlock_periph p);
void hwlock_release(enum hwlock_periph p);
#endif /* MODULES_H */
#include "epicardium.h"
#include "leds.h"
#include "modules.h"
#include "os/work_queue.h"
#include "FreeRTOS.h"
#include "timers.h"
#include <math.h>
static uint8_t _personal_state_enabled = 0;
static uint8_t personal_state = STATE_NONE;
static uint8_t personal_state_persistent = 0;
static int led_animation_ticks = 0;
static int led_animation_state = 0;
static TimerHandle_t led_timer;
static StaticTimer_t led_timer_buffer;
static void worktick(void *data);
static const int led_animation_rate = 1000 / 25; /* 25Hz -> 40ms*/
int personal_state_enabled()
{
return _personal_state_enabled;
}
static void tick(TimerHandle_t xTimer)
{
workqueue_schedule(worktick, NULL);
}
int epic_personal_state_set(uint8_t state, bool persistent)
{
if (state > STATE_CAMP)
return -EINVAL;
led_animation_state = 0;
led_animation_ticks = 0;
personal_state = state;
uint8_t was_enabled = _personal_state_enabled;
_personal_state_enabled = (state != STATE_NONE);
personal_state_persistent = persistent;
if (!was_enabled && _personal_state_enabled) {
// Activate
if (!led_timer) {
led_timer = xTimerCreateStatic(
"personal state",
led_animation_rate / portTICK_PERIOD_MS,
pdTRUE,
NULL,
tick,
&led_timer_buffer
);
// since &poll_timer_buffer is not NULL, xTimerCreateStatic should allways succeed, so
// we don't need to check for poll_timer being NULL.
}
if (xTimerIsTimerActive(led_timer) == pdFALSE) {
xTimerStart(led_timer, 0);
}
} else if (was_enabled && !_personal_state_enabled) {
// Deactivate
xTimerStop(led_timer, 0);
// TODO: we might need a lock here to avoid a race condition
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
epic_leds_update();
}
return 0;
}
int epic_personal_state_get()
{
return personal_state;
}
int epic_personal_state_is_persistent()
{
return personal_state_persistent;
}
static void worktick(void *data)
{
if (_personal_state_enabled) {
led_animation_ticks++;
if (personal_state == STATE_NO_CONTACT) {
leds_prep(PERSONAL_STATE_LED, 255, 0, 0);
} else if (personal_state == STATE_CHAOS) {
if (led_animation_state == 0) {
leds_prep(PERSONAL_STATE_LED, 0, 0, 255);
if (led_animation_ticks >
(200 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 1;
}
} else if (led_animation_state == 1) {
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
if (led_animation_ticks >
(300 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 2;
}
} else if (led_animation_state == 2) {
leds_prep(PERSONAL_STATE_LED, 0, 0, 255);
if (led_animation_ticks >
(1000 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 3;
}
} else if (led_animation_state == 3) {
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
if (led_animation_ticks >
(300 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 0;
}
}
} else if (personal_state == STATE_COMMUNICATION) {
if (led_animation_state == 0) {
leds_prep(PERSONAL_STATE_LED, 255, 255, 0);
if (led_animation_ticks >
(1000 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 1;
}
} else if (led_animation_state == 1) {
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
if (led_animation_ticks >
(300 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 0;
}
}
} else if (personal_state == STATE_CAMP) {
leds_prep_hsv(
PERSONAL_STATE_LED,
120.0f,
1.0f,
fabs(sin(
led_animation_ticks /
(float)(1000 / led_animation_rate))));
}
epic_leds_update();
}
}
#include <stdio.h>
#include "max32665.h"
#include "gcr_regs.h"
#include "pmic.h"
#include "MAX77650-Arduino-Library.h"
#include "FreeRTOS.h"
#include "task.h"
#include "epicardium.h"
#include "modules.h"
#include "modules/log.h"
/* Task ID for the pmic handler */
static TaskHandle_t pmic_task_id = NULL;
void pmic_interrupt_callback(void *_)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (pmic_task_id != NULL) {
vTaskNotifyGiveFromISR(pmic_task_id, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
void vPmicTask(void *pvParameters)
{
int count = 0;
portTickType delay = portMAX_DELAY;
pmic_task_id = xTaskGetCurrentTaskHandle();
while (1) {
ulTaskNotifyTake(pdTRUE, delay);
if (count == PMIC_PRESS_SLEEP) {
LOG_ERR("pmic", "Sleep [[ Unimplemented ]]");
}
if (count == PMIC_PRESS_POWEROFF) {
LOG_INFO("pmic", "Poweroff");
MAX77650_setSFT_RST(0x2);
}
uint8_t int_flag = MAX77650_getINT_GLBL();
if (int_flag & MAX77650_INT_nEN_F) {
/* Button was pressed */
count = 0;
delay = portTICK_PERIOD_MS * 100;
}
if (int_flag & MAX77650_INT_nEN_R) {
/* Button was pressed */
if (count < PMIC_PRESS_SLEEP) {
LOG_INFO("pmic", "Reset");
/*
* Give the UART fifo time to clear.
* TODO: Do this properly
*/
for (int i = 0; i < 0x1000000; i++) {
__asm volatile("nop");
}
MXC_GCR->rstr0 = MXC_F_GCR_RSTR0_SYSTEM;
}
count = 0;
delay = portMAX_DELAY;
}
/* TODO: Remove when all interrupts are handled */
if (int_flag & ~(MAX77650_INT_nEN_F | MAX77650_INT_nEN_R)) {
LOG_WARN("pmic", "Unhandled PMIC Interrupt: %x",
int_flag);
}
if (delay != portMAX_DELAY) {
count += 1;
}
}
}
#include <stdint.h>
#include <stdio.h>
#include "max32665.h"
#include "cdcacm.h"
#include "uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "modules.h"
#include "modules/log.h"
#include "api/interrupt-sender.h"
/* Task ID for the serial handler */
TaskHandle_t serial_task_id = NULL;
/* The serial console in use (UART0) */
extern mxc_uart_regs_t *ConsoleUart;
/* Read queue, filled by both UART and CDCACM */
static QueueHandle_t read_queue;
/*
* API-call to write a string. Output goes to both CDCACM and UART
*/
void epic_uart_write_str(const char *str, intptr_t length)
{
UART_Write(ConsoleUart, (uint8_t *)str, length);
cdcacm_write((uint8_t *)str, length);
}
/*
* Blocking API-call to read a character from the queue.
*/
char epic_uart_read_chr(void)
{
char chr;
xQueueReceive(read_queue, &chr, portMAX_DELAY);
return chr;
}
/* Interrupt handler needed for SDK UART implementation */
void UART0_IRQHandler(void)
{
UART_Handler(ConsoleUart);
}
static void uart_callback(uart_req_t *req, int error)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(serial_task_id, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
static void enqueue_char(char chr)
{
if (chr == 0x3) {
/* Control-C */
api_interrupt_trigger(EPIC_INT_CTRL_C);
}
if (chr == 0x0e) {
/* Control-N */
api_interrupt_trigger(EPIC_INT_BHI160_TEST);
}
if (xQueueSend(read_queue, &chr, 100) == errQUEUE_FULL) {
/* Queue overran, wait a bit */
vTaskDelay(portTICK_PERIOD_MS * 50);
}
}
void vSerialTask(void *pvParameters)
{
static uint8_t buffer[sizeof(char) * SERIAL_READ_BUFFER_SIZE];
static StaticQueue_t read_queue_data;
serial_task_id = xTaskGetCurrentTaskHandle();
/* Setup read queue */
read_queue = xQueueCreateStatic(
SERIAL_READ_BUFFER_SIZE, sizeof(char), buffer, &read_queue_data
);
/* Setup UART interrupt */
NVIC_ClearPendingIRQ(UART0_IRQn);
NVIC_DisableIRQ(UART0_IRQn);
NVIC_SetPriority(UART0_IRQn, 6);
NVIC_EnableIRQ(UART0_IRQn);
unsigned char data;
uart_req_t read_req = {
.data = &data,
.len = 1,
.callback = uart_callback,
};
while (1) {
int ret = UART_ReadAsync(ConsoleUart, &read_req);
if (ret != E_NO_ERROR && ret != E_BUSY) {
LOG_ERR("serial", "error reading uart: %d", ret);
vTaskDelay(portMAX_DELAY);
}
ulTaskNotifyTake(pdTRUE, portTICK_PERIOD_MS * 1000);
if (read_req.num > 0) {
enqueue_char(*read_req.data);
}
while (UART_NumReadAvail(ConsoleUart) > 0) {
enqueue_char(UART_ReadByte(ConsoleUart));
}
while (cdcacm_num_read_avail() > 0) {
enqueue_char(cdcacm_read());
}
}
}
#include <string.h>
#include "FreeRTOS.h"
#include "semphr.h"
#include "queue.h"
#include "epicardium.h"
#include "modules/log.h"
#include "os/core.h"
#include "modules/stream.h"
#include "os/mutex.h"
/* Internal buffer of registered streams */
static struct stream_info *stream_table[SD_MAX];
/* Lock for modifying the stream info table */
static StaticSemaphore_t stream_table_lock_data;
static SemaphoreHandle_t stream_table_lock;
static struct mutex stream_table_lock;
int stream_init()
{
memset(stream_table, 0x00, sizeof(stream_table));
stream_table_lock =
xSemaphoreCreateMutexStatic(&stream_table_lock_data);
mutex_create(&stream_table_lock);
return 0;
}
int stream_register(int sd, struct stream_info *stream)
{
if (xSemaphoreTake(stream_table_lock, STREAM_MUTEX_WAIT) != pdTRUE) {
LOG_WARN("stream", "Lock contention error");
return -EBUSY;
}
int ret = 0;
mutex_lock(&stream_table_lock);
if (sd < 0 || sd >= SD_MAX) {
return -EINVAL;
ret = -EINVAL;
goto out;
}
if (stream_table[sd] != NULL) {
/* Stream already registered */
return -EACCES;
ret = -EACCES;
goto out;
}
stream_table[sd] = stream;
xSemaphoreGive(stream_table_lock);
return 0;
out:
mutex_unlock(&stream_table_lock);
return ret;
}
int stream_deregister(int sd, struct stream_info *stream)
{
if (xSemaphoreTake(stream_table_lock, STREAM_MUTEX_WAIT) != pdTRUE) {
LOG_WARN("stream", "Lock contention error");
return -EBUSY;
}
int ret = 0;
mutex_lock(&stream_table_lock);
if (sd < 0 || sd >= SD_MAX) {
return -EINVAL;
ret = -EINVAL;
goto out;
}
if (stream_table[sd] != stream) {
/* Stream registered by someone else */
return -EACCES;
ret = -EACCES;
goto out;
}
stream_table[sd] = NULL;
xSemaphoreGive(stream_table_lock);
return 0;
out:
mutex_unlock(&stream_table_lock);
return ret;
}
int epic_stream_read(int sd, void *buf, size_t count)
{
int ret = 0;
/*
* TODO: In theory, multiple reads on different streams can happen
* simulaneously. I don't know what the most efficient implementation
* of this would look like.
*/
if (xSemaphoreTake(stream_table_lock, STREAM_MUTEX_WAIT) != pdTRUE) {
LOG_WARN("stream", "Lock contention error");
return -EBUSY;
}
mutex_lock(&stream_table_lock);
if (sd < 0 || sd >= SD_MAX) {
return -EBADF;
ret = -EBADF;
goto out;
}
struct stream_info *stream = stream_table[sd];
if (stream == NULL) {
return -ENODEV;
ret = -ENODEV;
goto out;
}
/* Poll the stream, if a poll_stream function exists */
if (stream->poll_stream != NULL) {
int ret = stream->poll_stream();
ret = stream->poll_stream();
if (ret < 0) {
return ret;
goto out;
}
}
/* Check buffer size is a multiple of the data packet size */
if (count % stream->item_size != 0) {
return -EINVAL;
ret = -EINVAL;
goto out;
}
size_t i;
......@@ -107,6 +111,9 @@ int epic_stream_read(int sd, void *buf, size_t count)
}
}
xSemaphoreGive(stream_table_lock);
return i / stream->item_size;
ret = i / stream->item_size;
out:
mutex_unlock(&stream_table_lock);
return ret;
}
......@@ -4,14 +4,16 @@
#include <stdint.h>
#ifndef __SPHINX_DOC
/* stddef.h is not recognized by hawkmoth for some odd reason */
/* Some headers are not recognized by hawkmoth for some odd reason */
#include <stddef.h>
#else
typedef unsigned int size_t;
#endif /* __SPHINX_DOC */
#include "FreeRTOS.h"
#include "queue.h"
#else
typedef unsigned int size_t;
typedef int bool;
typedef void *QueueHandle_t;
#endif /* __SPHINX_DOC */
/* Time to wait for the descriptor table lock to become available */
#define STREAM_MUTEX_WAIT pdMS_TO_TICKS(100)
......@@ -25,6 +27,17 @@ typedef unsigned int size_t;
* Please keep IDs in sequential order.
*/
enum stream_descriptor {
/** BHI160 Accelerometer */
SD_BHI160_ACCELEROMETER,
/** BHI160 Magnetometer */
SD_BHI160_MAGNETOMETER,
/** BHI160 Orientation Sensor */
SD_BHI160_ORIENTATION,
/** BHI160 Gyroscope */
SD_BHI160_GYROSCOPE,
/** MAX30001 ECG */
SD_MAX30001_ECG,
SD_MAX86150,
/** Highest descriptor must always be ``SD_MAX``. */
SD_MAX,
};
......@@ -55,6 +68,12 @@ struct stream_info {
* The function registered here should never block for a longer time.
*/
int (*poll_stream)();
/**
* Set to true if the last write to ``queue`` failed because
* the queue was full.
*/
bool was_full;
};
/**
......
#include "gpio.h"
#include "FreeRTOS.h"
#include "timers.h"
static const gpio_cfg_t motor_pin = {
PORT_0, PIN_8, GPIO_FUNC_OUT, GPIO_PAD_NONE
};
static TimerHandle_t vibra_timer;
void epic_vibra_set(int status)
{
if (status) {
GPIO_OutSet(&motor_pin);
} else {
GPIO_OutClr(&motor_pin);
}
}
void vTimerCallback()
{
epic_vibra_set(0);
}
void epic_vibra_vibrate(int millis)
{
int ticks = millis * (configTICK_RATE_HZ / 1000);
epic_vibra_set(1);
vibra_timer =
xTimerCreate("vibratimer", ticks, pdFALSE, 0, vTimerCallback);
xTimerStart(vibra_timer, 0);
}
#include "os/core.h"
#include "os/config.h"
#include "epicardium.h"
#include <assert.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <stdio.h>
#define MAX_LINE_LENGTH 80
#define KEYS_PER_BLOCK 16
#define KEY_LENGTH 16
#define NOT_INT_MAGIC ((int)0x80000000)
// one key-value pair representing a line in the config
typedef struct {
char key[KEY_LENGTH];
// the value in the config file, if it's an integer.
// for strings it's set to NOT_INT_MAGIC
int value;
// the byte offset in the config file to read the value string
size_t value_offset;
} config_slot;
// a block of 16 config slots
// if more are needed, this becomes a linked list
typedef struct {
config_slot slots[KEYS_PER_BLOCK];
void *next;
} config_block;
static config_block *config_data = NULL;
// returns the config slot for a key name
static config_slot *find_config_slot(const char *key)
{
config_block *current = config_data;
while (current) {
for (int i = 0; i < KEYS_PER_BLOCK; i++) {
config_slot *k = &current->slots[i];
if (strcmp(k->key, key) == 0) {
// found what we're looking for
return k;
} else if (*k->key == '\0') {
// found the first empty key
return NULL;
}
}
current = current->next;
}
return NULL;
}
// returns the next available config slot, or allocates a new block if needed
static config_slot *allocate_config_slot()
{
config_block *current;
if (config_data == NULL) {
config_data = malloc(sizeof(config_block));
assert(config_data != NULL);
memset(config_data, 0, sizeof(config_block));
}
current = config_data;
while (true) {
for (int i = 0; i < KEYS_PER_BLOCK; i++) {
config_slot *k = &current->slots[i];
if (*k->key == '\0') {
return k;
}
}
// this block is full and there's no next allocated block
if (current->next == NULL) {
current->next = malloc(sizeof(config_block));
assert(current->next != NULL);
memset(current->next, 0, sizeof(config_block));
}
current = current->next;
}
}
// parses an int out of 'value' or returns NOT_INT_MAGIC
static int try_parse_int(const char *value)
{
char *endptr;
size_t len = strlen(value);
int v = strtol(value, &endptr, 0);
if (endptr != (value + len)) {
return NOT_INT_MAGIC;
}
return v;
}
// loads a key/value pair into a new config slot
static void add_config_pair(
const char *key, const char *value, int line_number, size_t value_offset
) {
if (strlen(key) > KEY_LENGTH - 1) {
LOG_WARN(
"card10.cfg",
"line:%d: too long - aborting",
line_number
);
return;
}
config_slot *slot = allocate_config_slot();
strncpy(slot->key, key, KEY_LENGTH);
slot->value = try_parse_int(value);
slot->value_offset = value_offset;
}
static void trim(char *str)
{
char *start = str;
while (*start && !isgraph(*start))
start++;
if (strlen(start) > 0) {
char *end = start + strlen(start) - 1;
while (*end && !isgraph(*end))
end--;
end[1] = 0;
}
memmove(str, start, strlen(start) + 1);
}
// parses one line of the config file
static void parse_line(char *line, int line_number, size_t line_offset)
{
char *line_start = line;
char *value_start = strchr(line, '=') + 1;
trim(line);
//printf(line);
if (*line == '#') {
//skip comments
return;
}
char *eq = strchr(line, '=');
if (!eq) {
if (*line) {
LOG_WARN(
"card10.cfg",
"line %d: syntax error",
line_number
);
}
return;
}
*eq = 0;
char *key = line;
trim(key);
if (*key == '\0') {
LOG_WARN("card10.cfg", "line %d: empty key", line_number);
return;
}
char *value = eq + 1;
trim(value);
if (*value == '\0') {
LOG_WARN(
"card10.cfg",
"line %d: empty value for option '%s'",
line_number,
key
);
return;
}
size_t value_offset = value_start - line_start + line_offset;
add_config_pair(key, value, line_number, value_offset);
}
typedef struct {
int line_number;
int file_offset;
int line_start;
char line[MAX_LINE_LENGTH + 1];
int line_length;
} parser_state;
int parse_character(char c, parser_state *s)
{
if (c != '\r' && c != '\n') {
if (s->line_length == MAX_LINE_LENGTH) {
LOG_WARN(
"card10.cfg",
"line:%d: too long - aborting",
s->line_number
);
return -1;
}
s->line[s->line_length++] = c;
} else {
s->line[s->line_length] = 0;
//printf("New line: %s (%d %d)\n", s->line, s->line_number, s->line_start);
parse_line(s->line, s->line_number, s->line_start);
s->line_length = 0;
s->line_start = s->file_offset + 1;
if (c == '\n') {
s->line_number++;
}
}
s->file_offset++;
return 0;
}
// parses the entire config file
void load_config(void)
{
LOG_DEBUG("card10.cfg", "loading...");
int fd = epic_file_open("card10.cfg", "r");
if (fd < 0) {
LOG_DEBUG(
"card10.cfg",
"loading failed: %s (%d)",
strerror(-fd),
fd
);
return;
}
/* Clear any existing configuration */
/* Don't free the blocks as we are most likely
* going to re-use all of them. */
config_block *current;
if (config_data != NULL) {
current = config_data;
while (true) {
memset(current->slots, 0, sizeof(current->slots));
if (current->next == NULL) {
break;
}
current = current->next;
}
}
char buf[128];
int nread;
parser_state s;
memset(&s, 0, sizeof(s));
s.line_number = 1;
do {
nread = epic_file_read(fd, buf, sizeof(buf));
int i;
for (i = 0; i < nread; i++) {
parse_character(buf[i], &s);
}
} while (nread == sizeof(buf));
parse_character('\n', &s);
epic_file_close(fd);
}
// opens the config file, seeks to seek_offset and reads buf_len bytes
// used for reading strings without storing them in memory
// since we don't need to optimize for that use case as much
static size_t read_config_offset(size_t seek_offset, char *buf, size_t buf_len)
{
int fd = epic_file_open("card10.cfg", "r");
if (fd < 0) {
LOG_DEBUG(
"card10.cfg",
"opening config failed: %s (%d)",
strerror(-fd),
fd
);
return 0;
}
int rc = epic_file_seek(fd, seek_offset, SEEK_SET);
if (rc < 0) {
LOG_ERR("card10.cfg", "seek2 failed (%d), aborting", rc);
return 0;
}
// one byte less to accommodate the 0 termination
int nread = epic_file_read(fd, buf, buf_len - 1);
buf[nread] = '\0';
epic_file_close(fd);
return nread;
}
// returns error if not found or invalid
int epic_config_get_integer(const char *key, int *value)
{
config_slot *slot = find_config_slot(key);
if (slot && slot->value != NOT_INT_MAGIC) {
*value = slot->value;
return 0;
}
return -ENOENT;
}
// returns default_value if not found or invalid
int config_get_integer_with_default(const char *key, int default_value)
{
int value;
int ret = epic_config_get_integer(key, &value);
if (ret) {
return default_value;
} else {
return value;
}
}
// returns error if not found
int epic_config_get_string(const char *key, char *buf, size_t buf_len)
{
config_slot *slot = find_config_slot(key);
if (!(slot && slot->value_offset)) {
return -ENOENT;
}
size_t nread = read_config_offset(slot->value_offset, buf, buf_len);
if (nread == 0) {
return -ENOENT;
}
char *end = buf;
while (*end && !iscntrl(*end))
end++;
*end = 0;
trim(buf);
return 0;
}
// returns dflt if not found, otherwise same pointer as buf
char *config_get_string_with_default(
const char *key, char *buf, size_t buf_len, char *dflt
) {
int ret = epic_config_get_string(key, buf, buf_len);
if (ret) {
return dflt;
} else {
return buf;
}
}
// returns error if not found or invalid
int epic_config_get_boolean(const char *key, bool *value)
{
int int_value;
int ret = epic_config_get_integer(key, &int_value);
if (ret == 0) {
*value = !!int_value;
return 0;
}
char buf[MAX_LINE_LENGTH];
ret = epic_config_get_string(key, buf, MAX_LINE_LENGTH);
if (ret < 0) {
return ret;
}
if (!strcasecmp(buf, "true")) {
*value = true;
return 0;
} else if (!strcasecmp(buf, "false")) {
*value = false;
return 0;
}
return -ERANGE;
}
// returns default_value if not found or invalid
bool config_get_boolean_with_default(const char *key, bool default_value)
{
bool value;
int ret = epic_config_get_boolean(key, &value);
if (ret) {
return default_value;
} else {
return value;
}
}
int epic_config_set_string(const char *key, const char *value_in)
{
char value[MAX_LINE_LENGTH + 1];
if (strlen(key) > MAX_LINE_LENGTH) {
return -EINVAL;
}
/* TODO: Change interface of trim to take the buffer and size directly */
if (strlen(value_in) > MAX_LINE_LENGTH) {
return -EINVAL;
}
strcpy(value, value_in);
trim(value);
if (snprintf(NULL, 0, "\n%s = %s\n", key, value) > MAX_LINE_LENGTH) {
return -EINVAL;
}
/* Check if key is sane. No control characters, spaces, equal signs or pounds allowed */
for (size_t i = 0; i < strlen(key); i++) {
char c = key[i];
if (!isgraph(c) || c == '=' || c == '#') {
return -EINVAL;
}
}
/* Check if value is sane. No control characters allowed */
for (size_t i = 0; i < strlen(value); i++) {
char c = value[i];
if (!isprint(c)) {
return -EINVAL;
}
}
config_slot *slot = find_config_slot(key);
bool present = slot && slot->value_offset;
int ret = 0;
if (!present) {
/* Easy case: We simply add the new option at the
* end of the file. */
char buf[MAX_LINE_LENGTH];
/* Leading new line because I'm lazy */
ret = snprintf(buf, sizeof(buf), "\n%s = %s\n", key, value);
if (ret < 0 || ret >= (int)sizeof(buf)) {
return -EINVAL;
}
int fd = epic_file_open("card10.cfg", "a");
if (fd < 0) {
LOG_DEBUG(
"card10.cfg",
"open for appending failed: %s (%d)",
strerror(-fd),
fd
);
return fd;
}
int write_ret = epic_file_write(fd, buf, strlen(buf));
if (write_ret < 0) {
LOG_DEBUG(
"card10.cfg",
"writing failed: %s (%d)",
strerror(-write_ret),
write_ret
);
}
if (write_ret < (int)strlen(buf)) {
LOG_DEBUG(
"card10.cfg",
"writing failed to write all bytes (%d of %d)",
write_ret,
strlen(buf)
);
}
ret = epic_file_close(fd);
if (ret < 0) {
LOG_DEBUG(
"card10.cfg",
"close failed: %s (%d)",
strerror(-ret),
ret
);
}
if (write_ret < 0) {
ret = write_ret;
}
if (ret < 0) {
goto out;
}
if (write_ret < (int)strlen(buf)) {
LOG_DEBUG(
"card10.cfg",
"writing failed to write all bytes (%d of %d)",
write_ret,
strlen(buf)
);
ret = -EIO;
goto out;
}
} else {
/* Complex case: The value is already somewhere in the file.
* We do not want to lose existing formatting or comments.
* Solution: Copy parts of the file, insert new value, copy
* rest, rename.
*/
char buf[MAX_LINE_LENGTH + 1];
int fd1 = -1;
int fd2 = -1;
ret = epic_config_get_string(key, buf, sizeof(buf));
if (ret == 0 && strcmp(buf, value) == 0) {
/* Nothing to do: the values are the same. */
return 0;
}
size_t nread = read_config_offset(
slot->value_offset, buf, sizeof(buf)
);
if (nread == 0) {
LOG_DEBUG("card10.cfg", "could not read old value");
ret = -EIO;
goto complex_out;
}
char *end = buf;
while (*end && (!iscntrl(*end) || isblank(*end)))
end++;
*end = 0;
int old_len = strlen(buf);
fd1 = epic_file_open("card10.cfg", "r");
if (fd1 < 0) {
LOG_DEBUG(
"card10.cfg",
"open for read failed: %s (%d)",
strerror(-fd1),
fd1
);
ret = fd1;
goto complex_out;
}
fd2 = epic_file_open("card10.nfg", "w");
if (fd2 < 0) {
LOG_DEBUG(
"card10.nfg",
"open for writing failed: %s (%d)",
strerror(-fd2),
fd2
);
ret = fd2;
goto complex_out;
}
/* Copy over slot->value_offset bytes */
int i = slot->value_offset;
while (i > 0) {
int n = i > (int)sizeof(buf) ? (int)sizeof(buf) : i;
ret = epic_file_read(fd1, buf, n);
if (ret < 0) {
LOG_DEBUG(
"card10.cfg",
"read failed: rc: %d",
ret
);
goto complex_out;
}
int ret2 = epic_file_write(fd2, buf, ret);
if (ret2 < 0) {
ret = ret2;
LOG_DEBUG(
"card10.nfg",
"write failed: rc: %d",
ret
);
goto complex_out;
}
i -= ret;
}
/* Insert new value into the new file */
ret = epic_file_write(fd2, value, strlen(value));
if (ret < 0) {
LOG_DEBUG("card10.nfg", "write failed: rc: %d", ret);
goto complex_out;
}
/* Skip the old value inside the old file */
epic_file_seek(fd1, old_len, SEEK_CUR);
/* Copy the rest of the old file to the new file */
while (true) {
int ret = epic_file_read(fd1, buf, sizeof(buf));
if (ret == 0) {
break;
}
if (ret < 0) {
LOG_DEBUG(
"card10.cfg",
"read failed: rc: %d",
ret
);
goto complex_out;
}
int ret2 = epic_file_write(fd2, buf, ret);
if (ret2 < 0) {
ret = ret2;
LOG_DEBUG(
"card10.nfg",
"write failed: rc: %d",
ret
);
goto complex_out;
}
if (ret < (int)sizeof(buf)) {
break;
}
}
complex_out:
if (fd1 >= 0) {
epic_file_close(fd1);
}
if (fd2 >= 0) {
int ret2 = epic_file_close(fd2);
if (ret >= 0) {
ret = ret2;
}
}
if (ret >= 0) {
epic_file_unlink("card10.cfg");
epic_file_rename("card10.nfg", "card10.cfg");
}
}
out:
/* Reload config so the new key or the changed value is available */
load_config();
return ret < 0 ? ret : 0;
}
#ifndef EPICARDIUM_MODULES_CONFIG_H_INCLUDED
#define EPICARDIUM_MODULES_CONFIG_H_INCLUDED
#include <stdbool.h>
#include <stddef.h>
//initialize configuration values and load card10.cfg
void load_config(void);
// returns default_value if not found or invalid
bool config_get_boolean_with_default(const char *key, bool default_value);
int config_get_integer_with_default(const char *key, int default_value);
// returns dflt if not found, otherwise same pointer as buf
char *config_get_string_with_default(const char *key, char *buf, size_t buf_len, char *dflt);
#endif//EPICARDIUM_MODULES_CONFIG_H_INCLUDED
#pragma once
/**
* Panic
* =====
* Panicking should be used in situations where no automatic recovery is
* possible of where a fatal bug was detected. Calling :c:func:`panic()` will
* show a message on the screen and serial console and then reboot the device.
*
* Keep in mind that screen space is limited and thus the message should be as
* concise as possible.
*/
/**
* Trigger a firmware panic.
*
* This function will not return but instead reboot the device. No
* synchronization of e.g. the filesystem is done so this could potentially lead
* to data loss.
*/
void panic(const char *format, ...)
__attribute__((noreturn, format(printf, 1, 2)));
/**
* Logging
* =======
* All logging macros take a "subsystem" and a log message. This subsystem
* argument should be used to uniquely identify where a message came from.
*
* **Example**:
*
* .. code-block:: cpp
*
* LOG_ERR("pmic",
* "Failed reading battery voltage: %s (%d)",
* strerror(-res),
* res);
*/
/* Whether to enable logging at all */
#ifndef LOG_ENABLE
#define LOG_ENABLE 1
#endif
/* Whether to enable even more verbose logging */
#ifndef LOG_ENABLE_DEBUG
#define LOG_ENABLE_DEBUG 0
#endif
/* Whether to enable colorful log */
#ifndef LOG_ENABLE_COLOR
#define LOG_ENABLE_COLOR 1
#endif
#define SEV_DEBUG "D"
#define SEV_INFO "I"
#define SEV_WARN "W"
#define SEV_ERR "E"
#define SEV_CRIT "C"
#if LOG_ENABLE && !defined(__SPHINX_DOC)
int log_msg(const char *subsys, const char *format, ...)
__attribute__((format(printf, 2, 3)));
#if LOG_ENABLE_DEBUG
#define LOG_DEBUG(subsys, format, ...) \
log_msg(subsys, SEV_DEBUG format, ##__VA_ARGS__)
#else /* LOG_ENABLE_DEBUG */
#define LOG_DEBUG(subsys, format, ...)
#endif /* LOG_ENABLE_DEBUG */
#define LOG_INFO(subsys, format, ...) \
log_msg(subsys, SEV_INFO format, ##__VA_ARGS__)
#define LOG_WARN(subsys, format, ...) \
log_msg(subsys, SEV_WARN format, ##__VA_ARGS__)
#define LOG_ERR(subsys, format, ...) \
log_msg(subsys, SEV_ERR format, ##__VA_ARGS__)
#define LOG_CRIT(subsys, format, ...) \
log_msg(subsys, SEV_CRIT format, ##__VA_ARGS__)
#else /* LOG_ENABLE */
/** */
#define LOG_INFO(subsys, format, ...) 0
/** */
#define LOG_WARN(subsys, format, ...) 0
/** */
#define LOG_ERR(subsys, format, ...) 0
/** */
#define LOG_CRIT(subsys, format, ...) 0
/** Only prints when debug logging is enabled in the meson configuration. */
#define LOG_DEBUG(subsys, format, ...) 0
inline __attribute__((format(printf, 2, 3))) int
log_msg(const char *subsys, const char *format, ...)
{
return 0;
}
#endif /* LOG_ENABLE */
#include "modules/log.h"
#include "os/core.h"
#include "FreeRTOS.h"
#include "task.h"
......
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 */