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
  • Stormwind
  • 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
  • dx/somewhat-more-dynamic-config
  • 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
  • patch-1
  • plaetzchen/ios-workaround
  • rahix/bhi
  • rahix/bma
  • rahix/proper-sleep
  • renze/hatchery_apps
  • renze/safe_mode
  • schleicher-test
  • schneider/ble-buffers
  • schneider/ble-stability
  • schneider/ble-stability-new-phy
  • schneider/bonding
  • schneider/bootloader-update-9a0d158
  • schneider/bsec
  • schneider/fundamental-test
  • schneider/max30001
  • schneider/max30001-epicaridum
  • schneider/max30001-pycardium
  • schneider/maxim-sdk-update
  • schneider/mp-for-old-bl
  • schneider/schleicher-test
  • schneider/stream-locks
  • bootloader-v1
  • release-1
  • v0.0
  • v1.0
  • v1.1
  • v1.10
  • v1.11
  • v1.12
  • v1.2
  • v1.3
  • v1.4
  • v1.5
  • v1.6
  • v1.7
  • v1.8
  • v1.9
58 results
Show changes
Showing
with 982 additions and 1232 deletions
#include "epicardium.h"
#include "modules/modules.h"
#include "modules/log.h"
#include "card10.h"
#include "bme680.h"
#include "bosch.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#define HEATR_TEMP 320
#define HEATR_DUR 150
static bool initialized;
static struct bme680_dev bme;
static int convert_error(int8_t error)
{
switch (error) {
case BME680_OK:
return 0;
case BME680_E_NULL_PTR:
return EFAULT;
case BME680_E_COM_FAIL:
return EIO;
case BME680_E_DEV_NOT_FOUND:
return ENODEV;
case BME680_E_INVALID_LENGTH:
return EINVAL;
default:
return 1;
}
}
int epic_bme680_init()
{
int8_t result = BME680_OK;
if (initialized) {
return 0;
}
if (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)) < 0) {
return -EBUSY;
}
bme.dev_id = BME680_I2C_ADDR_PRIMARY;
bme.intf = BME680_I2C_INTF;
bme.read = card10_bosch_i2c_read;
bme.write = card10_bosch_i2c_write;
bme.delay_ms = card10_bosch_delay;
/*
* amb_temp can be set to 25 prior to configuring the gas sensor
* or by performing a few temperature readings without operating
* the gas sensor.
*/
bme.amb_temp = 25;
result = bme680_init(&bme);
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_init error: %d\n", result);
goto err;
}
/*
* Select the power mode. Must be set before writing the sensor
* configuration
*/
bme.power_mode = BME680_FORCED_MODE;
/* Set the temperature, pressure and humidity settings */
bme.tph_sett.os_hum = BME680_OS_2X;
bme.tph_sett.os_pres = BME680_OS_4X;
bme.tph_sett.os_temp = BME680_OS_8X;
bme.tph_sett.filter = BME680_FILTER_SIZE_3;
/* Set the remaining gas sensor settings and link the heating profile */
bme.gas_sett.run_gas = BME680_ENABLE_GAS_MEAS;
/* Create a ramp heat waveform in 3 steps */
bme.gas_sett.heatr_temp = HEATR_TEMP; /* degree Celsius */
bme.gas_sett.heatr_dur = HEATR_DUR; /* milliseconds */
/* Set the required sensor settings needed */
uint16_t settings_sel = BME680_OST_SEL | BME680_OSP_SEL |
BME680_OSH_SEL | BME680_FILTER_SEL |
BME680_GAS_SENSOR_SEL;
result = bme680_set_sensor_settings(settings_sel, &bme);
if (result != BME680_OK) {
LOG_ERR("bme680",
"bme680_set_sensor_settings error: %d\n",
result);
goto err;
}
initialized = true;
result = BME680_OK;
err:
hwlock_release(HWLOCK_I2C);
return -convert_error(result);
}
int epic_bme680_deinit()
{
if (!initialized) {
return 0;
}
if (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)) < 0) {
return -EBUSY;
}
int8_t result = bme680_soft_reset(&bme);
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_soft_reset error: %d\n", result);
}
hwlock_release(HWLOCK_I2C);
initialized = false;
return 0;
}
int epic_bme680_read_sensors(struct bme680_sensor_data *data)
{
int8_t result = BME680_OK;
if (!initialized) {
LOG_ERR("bme680", "bme680 sensor not initialized");
return -EINVAL;
}
if (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)) < 0) {
return -EBUSY;
}
uint16_t profile_dur = 0;
bme680_get_profile_dur(&profile_dur, &bme);
result = bme680_set_sensor_mode(&bme); /* Trigger a measurement */
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_set_sensor_mode error: %d\n", result);
goto err;
}
/*
* Wait for the measurement to complete. Release the I2C lock in the
* meantime.
*/
hwlock_release(HWLOCK_I2C);
vTaskDelay(pdMS_TO_TICKS(profile_dur));
if (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)) < 0) {
return -EBUSY;
}
struct bme680_field_data raw_data;
result = bme680_get_sensor_data(&raw_data, &bme);
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_get_sensor_data error: %d\n", result);
goto err;
}
data->temperature = (float)raw_data.temperature / 100.0f;
data->humidity = raw_data.humidity / 1000.0f;
data->pressure = raw_data.pressure / 100.0f;
data->gas_resistance = raw_data.gas_resistance;
result = BME680_OK;
err:
hwlock_release(HWLOCK_I2C);
return -convert_error(result);
}
#include "modules/log.h"
#include "modules/config.h"
#include "modules/filesystem.h"
#include <assert.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define CONFIG_MAX_LINE_LENGTH 80
enum OptionType {
OptionType_Boolean,
OptionType_Int,
OptionType_Float,
OptionType_String,
};
struct config_option {
const char *name;
enum OptionType type;
union {
bool boolean;
long integer;
double floating_point;
char *string;
} value;
};
static struct config_option s_options[_EpicOptionCount] = {
/* clang-format off */
#define INIT_Boolean(v) { .boolean = (v) }
#define INIT_Int(v) { .integer = (v) }
#define INIT_Float(v) { .floating_point = (v) }
#define INIT_String(v) { .string = (v) }
#define INIT_(tp, v) INIT_ ## tp (v)
#define INIT(tp, v) INIT_ (tp, v)
#define CARD10_SETTING(identifier, spelling, tp, default_value) \
[Option ## identifier] = { .name = (spelling), \
.type = OptionType_ ## tp, \
.value = INIT(tp, (default_value)) },
#include "modules/config.def"
/* clang-format on */
};
static struct config_option *findOption(const char *key)
{
for (int i = 0; i < _EpicOptionCount; ++i) {
if (!strcmp(key, s_options[i].name)) {
return &s_options[i];
}
}
return NULL;
}
static bool set_bool(struct config_option *opt, const char *value)
{
bool val;
if (!strcmp(value, "1")) {
val = true;
} else if (!strcmp(value, "true")) {
val = true;
} else if (!strcmp(value, "0")) {
val = false;
} else if (!strcmp(value, "false")) {
val = false;
} else {
return false;
}
opt->value.boolean = val;
LOG_DEBUG(
"card10.cfg",
"setting '%s' to %s",
opt->name,
val ? "true" : "false"
);
return true;
}
static bool set_int(struct config_option *opt, const char *value)
{
char *endptr;
size_t len = strlen(value);
int v = strtol(value, &endptr, 0);
if (endptr != (value + len)) {
return false;
}
opt->value.integer = v;
LOG_DEBUG("card10.cfg", "setting '%s' to %d (0x%08x)", opt->name, v, v);
return true;
}
static bool set_float(struct config_option *opt, const char *value)
{
char *endptr;
size_t len = strlen(value);
double v = strtod(value, &endptr);
if (endptr != (value + len)) {
return false;
}
opt->value.floating_point = v;
LOG_DEBUG("card10.cfg", "setting '%s' to %f", opt->name, v);
return true;
}
const char *elide(const char *str)
{
static char ret[21];
size_t len = strlen(str);
if (len <= 20) {
return str;
}
strncpy(ret, str, 17);
ret[17] = '.';
ret[18] = '.';
ret[19] = '.';
ret[20] = '\0';
return ret;
}
static bool set_string(struct config_option *opt, const char *value)
{
//this leaks, but the lifetime of these ends when epicardium exits, so...
char *leaks = strdup(value);
opt->value.string = leaks;
LOG_DEBUG("card10.cfg", "setting '%s' to %s", opt->name, elide(leaks));
return true;
}
static void configure(const char *key, const char *value, int lineNumber)
{
struct config_option *opt = findOption(key);
if (!opt) {
//invalid key
LOG_WARN(
"card10.cfg",
"line %d: ignoring unknown option '%s'",
lineNumber,
key
);
return;
}
bool ok = false;
switch (opt->type) {
case OptionType_Boolean:
ok = set_bool(opt, value);
break;
case OptionType_Int:
ok = set_int(opt, value);
break;
case OptionType_Float:
ok = set_float(opt, value);
break;
case OptionType_String:
ok = set_string(opt, value);
break;
default:
assert(0 && "unreachable");
}
if (!ok) {
LOG_WARN(
"card10.cfg",
"line %d: ignoring invalid value '%s' for option '%s'",
lineNumber,
value,
key
);
}
}
static void doline(char *line, char *eol, int lineNumber)
{
//skip leading whitespace
while (*line && isspace((int)*line))
++line;
char *key = line;
if (*key == '#') {
//skip comments
return;
}
char *eq = strchr(line, '=');
if (!eq) {
if (*key) {
LOG_WARN(
"card10.cfg",
"line %d (%s): syntax error",
lineNumber,
elide(line)
);
}
return;
}
char *e_key = eq - 1;
//skip trailing whitespace in key
while (e_key > key && isspace((int)*e_key))
--e_key;
e_key[1] = '\0';
if (*key == '\0') {
LOG_WARN("card10.cfg", "line %d: empty key", lineNumber);
return;
}
char *value = eq + 1;
//skip leading whitespace
while (*value && isspace((int)*value))
++value;
char *e_val = eol - 1;
//skip trailing whitespace
while (e_val > value && isspace((int)*e_val))
--e_val;
if (*value == '\0') {
LOG_WARN(
"card10.cfg",
"line %d: empty value for option '%s'",
lineNumber,
key
);
return;
}
configure(key, value, lineNumber);
}
bool config_get_boolean(enum EpicConfigOption option)
{
struct config_option *opt = &s_options[option];
assert(opt->type == OptionType_Boolean);
return opt->value.boolean;
}
long config_get_integer(enum EpicConfigOption option)
{
struct config_option *opt = &s_options[option];
assert(opt->type == OptionType_Int);
return opt->value.integer;
}
double config_get_float(enum EpicConfigOption option)
{
struct config_option *opt = &s_options[option];
assert(opt->type == OptionType_Float);
return opt->value.floating_point;
}
const char *config_get_string(enum EpicConfigOption option)
{
struct config_option *opt = &s_options[option];
assert(opt->type == OptionType_String);
return opt->value.string;
}
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;
}
char buf[CONFIG_MAX_LINE_LENGTH + 1];
int lineNumber = 0;
int nread;
do {
nread = epic_file_read(fd, buf, CONFIG_MAX_LINE_LENGTH);
if (nread < CONFIG_MAX_LINE_LENGTH) {
//add fake EOL to ensure termination
buf[nread++] = '\n';
}
//zero-terminate buffer
buf[nread] = '\0';
char *line = buf;
char *eol = NULL;
int last_eol = 0;
while (line) {
//line points one character past the las (if any) '\n' hence '- 1'
last_eol = line - buf - 1;
eol = strchr(line, '\n');
++lineNumber;
if (eol) {
*eol = '\0';
doline(line, eol, lineNumber);
line = eol + 1;
} else {
if (line == buf) {
//line did not fit into buf
LOG_WARN(
"card10.cfg",
"line:%d: too long - aborting",
lineNumber
);
return;
} else {
int seek_back = last_eol - nread;
LOG_DEBUG(
"card10.cfg",
"nread, last_eol, seek_back: %d,%d,%d",
nread,
last_eol,
seek_back
);
assert(seek_back <= 0);
if (seek_back) {
int rc = epic_file_seek(
fd,
seek_back,
SEEK_CUR
);
if (rc < 0) {
LOG_ERR("card10.cfg",
"seek failed, aborting");
return;
}
char newline;
rc = epic_file_read(
fd, &newline, 1
);
if (rc < 0 || newline != '\n') {
LOG_ERR("card10.cfg",
"seek failed, aborting");
LOG_DEBUG(
"card10.cfg",
"seek failed at read-back of newline: rc: %d read: %d",
rc,
(int)newline
);
return;
}
}
break;
}
}
}
} while (nread == sizeof(buf));
epic_file_close(fd);
}
#ifndef CARD10_SETTING
# define CARD10_SETTING(identifier, spelling, type, default_value)
#endif
CARD10_SETTING(ExecuteElf, "execute_elf", Boolean, false)
//CARD10_SETTING(Nick, "nick", String, "an0n")
//CARD10_SETTING(Timeout, "timeout", Integer, 123)
//CARD10_SETTING(Dampening, "dampening", Float, 420)
#undef CARD10_SETTING
#ifndef EPICARDIUM_MODULES_CONFIG_H_INCLUDED
#define EPICARDIUM_MODULES_CONFIG_H_INCLUDED
#include <stdbool.h>
enum EpicConfigOption {
#define CARD10_SETTING(identifier, spelling, type, default_value) Option ## identifier,
#include "modules/config.def"
_EpicOptionCount
};
//initialize configuration values and load card10.cfg
void load_config(void);
bool config_get_boolean(enum EpicConfigOption option);
long config_get_integer(enum EpicConfigOption option);
double config_get_float(enum EpicConfigOption option);
const char* config_get_string(enum EpicConfigOption option);
#endif//EPICARDIUM_MODULES_CONFIG_H_INCLUDED
#include "display.h"
#include "Fonts/fonts.h"
#include "FreeRTOS.h"
#include "LCD_Driver.h"
#include "epicardium.h"
#include "gfx.h"
#include "gpio.h"
#include "task.h"
#include "tmr.h"
#include "tmr_utils.h"
static TaskHandle_t lock = NULL;
static int check_lock()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
if (task != lock) {
return -EBUSY;
} else {
return 0;
}
}
int epic_disp_print(
int16_t posx,
int16_t posy,
const char *pString,
uint16_t fg,
uint16_t bg
) {
return epic_disp_print_adv(DISP_FONT20, posx, posy, pString, fg, bg);
}
static const sFONT *font_map[] = {
[DISP_FONT8] = &Font8, [DISP_FONT12] = &Font12,
[DISP_FONT16] = &Font16, [DISP_FONT20] = &Font20,
[DISP_FONT24] = &Font24,
};
int epic_disp_print_adv(
uint8_t font,
int16_t posx,
int16_t posy,
const char *pString,
uint16_t fg,
uint16_t bg
) {
int cl = check_lock();
if (font >= (sizeof(font_map) / sizeof(sFONT *))) {
return -EINVAL;
}
if (cl < 0) {
return cl;
} else {
gfx_puts(
font_map[font],
&display_screen,
posx,
posy,
pString,
fg,
bg
);
return 0;
}
}
int epic_disp_clear(uint16_t color)
{
int cl = check_lock();
if (cl < 0) {
return cl;
} else {
gfx_clear_to_color(&display_screen, color);
return 0;
}
}
int epic_disp_pixel(int16_t x, int16_t y, uint16_t color)
{
int cl = check_lock();
if (cl < 0) {
return cl;
} else {
gfx_setpixel(&display_screen, x, y, color);
return 0;
}
}
int epic_disp_line(
int16_t xstart,
int16_t ystart,
int16_t xend,
int16_t yend,
uint16_t color,
enum disp_linestyle linestyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0) {
return cl;
} else {
/* TODO add linestyle support to gfx code */
gfx_line(
&display_screen,
xstart,
ystart,
xend,
yend,
pixelsize,
color
);
return 0;
}
}
int epic_disp_rect(
int16_t xstart,
int16_t ystart,
int16_t xend,
int16_t yend,
uint16_t color,
enum disp_fillstyle fillstyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0)
return cl;
switch (fillstyle) {
case FILLSTYLE_EMPTY:
gfx_rectangle(
&display_screen,
xstart,
ystart,
xend - xstart,
yend - ystart,
pixelsize,
color
);
break;
case FILLSTYLE_FILLED:
gfx_rectangle_fill(
&display_screen,
xstart,
ystart,
xend - xstart,
yend - ystart,
color
);
break;
}
return 0;
}
int epic_disp_circ(
int16_t x,
int16_t y,
uint16_t rad,
uint16_t color,
enum disp_fillstyle fillstyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0)
return cl;
switch (fillstyle) {
case FILLSTYLE_EMPTY:
gfx_circle(&display_screen, x, y, rad, pixelsize, color);
break;
case FILLSTYLE_FILLED:
gfx_circle_fill(&display_screen, x, y, rad, color);
break;
}
return 0;
}
int epic_disp_update()
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
gfx_update(&display_screen);
return 0;
}
int epic_disp_framebuffer(union disp_framebuffer *fb)
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
LCD_Set(fb->raw, sizeof(fb->raw));
return 0;
}
int epic_disp_backlight(uint16_t brightness)
{
/* TODO: lock? */
LCD_SetBacklight(brightness);
return 0;
}
int epic_disp_open()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
if (lock == task) {
return 0;
} else if (lock == NULL) {
lock = task;
return 0;
} else {
return -EBUSY;
}
}
int epic_disp_close()
{
if (check_lock() < 0 && lock != NULL) {
return -EBUSY;
} else {
lock = NULL;
return 0;
}
}
void disp_forcelock()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
lock = task;
}
#include "epicardium.h"
#include "api/dispatcher.h"
#include "api/interrupt-sender.h"
#include "usb/epc_usb.h"
#include "modules/filesystem.h"
#include "modules/log.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 "display.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 "trng.h"
#include "wdt.h"
/*
......@@ -74,40 +76,34 @@ int hardware_early_init(void)
*/
portexpander_init();
/*
* RNG
*/
TRNG_Init(NULL);
/*
* Buttons
*/
PB_Init();
/* Enable 32 kHz output */
while (RTC_SquareWave(
MXC_RTC,
SQUARE_WAVE_ENABLED,
F_32KHZ,
NOISE_IMMUNE_MODE,
NULL) == E_BUSY
)
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() < 1546300800UL) {
if (RTC_GetSecond() < 1546300800L) {
epic_rtc_set_milliseconds(1546300800UL * 1000);
}
/*
* SPI for ECG
*/
const sys_cfg_spi_t spi17y_master_cfg = {
.map = MAP_A,
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");
......@@ -116,10 +112,9 @@ int hardware_early_init(void)
}
/*
* The bootloader has already initialized the display, so we only need
* to do the bare minimum here (mostly the gfx datastructures).
* Display
*/
display_init_slim();
disp_init();
/*
* RGB LEDs
......@@ -171,7 +166,7 @@ int hardware_early_init(void)
/*
* API Dispatcher & API Interrupts
*/
api_interrupt_init();
interrupt_init();
api_dispatcher_init();
/*
......@@ -194,6 +189,18 @@ int hardware_early_init(void)
*/
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;
......@@ -232,7 +239,7 @@ int hardware_reset(void)
/*
* API Dispatcher & API Interrupts
*/
api_interrupt_init();
interrupt_init();
api_dispatcher_init();
/*
......@@ -263,7 +270,7 @@ int hardware_reset(void)
/*
* Display
*/
display_init_slim();
disp_init();
epic_disp_backlight(20);
/*
......@@ -276,12 +283,19 @@ int hardware_reset(void)
*/
epic_bhi160_disable_all_sensors();
epic_max30001_disable_sensor();
epic_max86150_disable_sensor();
/*
* BME680 Sensor
* BLE
*/
epic_bme680_deinit();
epic_max30001_disable_sensor();
/* Reset advertisement data */
ble_adv_setup();
/* Start advertising again if needed */
epic_ble_set_mode(false, false);
return 0;
}
#include "modules/log.h"
#include "os/core.h"
#include "modules/modules.h"
#include "os/mutex.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include <errno.h>
static StaticSemaphore_t hwlock_mutex_data[_HWLOCK_MAX];
static SemaphoreHandle_t hwlock_mutex[_HWLOCK_MAX];
/* Which task is holding the lock */
static TaskHandle_t hwlock_tasks[_HWLOCK_MAX];
static struct mutex hwlock_mutex[_HWLOCK_MAX] = { { 0 } };
void hwlock_init(void)
{
for (int i = 0; i < _HWLOCK_MAX; i++) {
hwlock_mutex[i] =
xSemaphoreCreateMutexStatic(&hwlock_mutex_data[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]);
}
}
int hwlock_acquire(enum hwlock_periph p, TickType_t wait)
void hwlock_acquire(enum hwlock_periph p)
{
if (p >= _HWLOCK_MAX) {
return -EINVAL;
assert(p < _HWLOCK_MAX);
mutex_lock(&hwlock_mutex[p]);
}
TaskHandle_t task = xTaskGetCurrentTaskHandle();
if (xSemaphoreTake(hwlock_mutex[p], wait) != pdTRUE) {
/* Don't print warnings for 0 wait acquires */
if (wait == 0) {
return -EBUSY;
}
LOG_WARN(
"hwlock",
"Lock %u is busy! Held by \"%s\" and attempted to accquire by \"%s\"",
p,
pcTaskGetName(hwlock_tasks[p]),
pcTaskGetName(task)
);
LOG_DEBUG(
"hwlock",
"...attempted to lock from pc %p",
__builtin_return_address(0)
);
int hwlock_acquire_nonblock(enum hwlock_periph p)
{
assert(p < _HWLOCK_MAX);
if (mutex_trylock(&hwlock_mutex[p])) {
return 0;
} else {
return -EBUSY;
}
hwlock_tasks[p] = task;
return 0;
}
int hwlock_release(enum hwlock_periph p)
void hwlock_release(enum hwlock_periph p)
{
int ret = 0;
if (p >= _HWLOCK_MAX) {
return -EINVAL;
}
if (hwlock_tasks[p] != xTaskGetCurrentTaskHandle()) {
LOG_ERR("hwlock",
"Lock %u is released by task \"%s\" while it was acquired by \"%s\"",
p,
pcTaskGetName(NULL),
pcTaskGetName(hwlock_tasks[p]));
ret = -EACCES;
}
if (xSemaphoreGive(hwlock_mutex[p]) != pdTRUE) {
LOG_ERR("hwlock", "Lock %u not released correctly.", p);
ret = -EINVAL;
}
return ret;
assert(p < _HWLOCK_MAX);
mutex_unlock(&hwlock_mutex[p]);
}
#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(
'bhi.c',
'bme680.c',
'buttons.c',
'config.c',
'dispatcher.c',
'display.c',
'fileops.c',
'gpio.c',
'hardware.c',
'hw-lock.c',
'leds.c',
'lifecycle.c',
'light_sensor.c',
'log.c',
'max30001.c',
'mutex.c',
'panic.c',
'personal_state.c',
'pmic.c',
'rtc.c',
'serial.c',
'sleep.c',
'stream.c',
'trng.c',
'usb.c',
'vibra.c',
'watchdog.c',
'ws2812.c'
)
......@@ -3,87 +3,25 @@
#include "FreeRTOS.h"
#include "gpio.h"
#include "modules/mutex.h"
#include "os/mutex.h"
#include "epicardium.h"
#include <stdint.h>
#include <stdbool.h>
/* ---------- Panic -------------------------------------------------------- */
void panic(const char *format, ...)
__attribute__((noreturn, format(printf, 1, 2)));
/* ---------- Dispatcher --------------------------------------------------- */
void vApiDispatcher(void *pvParameters);
void dispatcher_mutex_init(void);
extern struct mutex api_mutex;
extern TaskHandle_t dispatcher_task_id;
/* ---------- Hardware Init & Reset ---------------------------------------- */
int hardware_early_init(void);
int hardware_init(void);
int hardware_reset(void);
/* ---------- Lifecycle ---------------------------------------------------- */
void vLifecycleTask(void *pvParameters);
void return_to_menu(void);
/* ---------- Serial ------------------------------------------------------- */
#define SERIAL_READ_BUFFER_SIZE 128
#define SERIAL_WRITE_STREAM_BUFFER_SIZE 512
void serial_init();
void vSerialTask(void *pvParameters);
void serial_enqueue_char(char chr);
void serial_flush(void);
extern TaskHandle_t serial_task_id;
/* Turn off the print queue and do prints synchroneous from now on. */
void serial_return_to_synchronous();
// For the eSetBit xTaskNotify task semaphore trigger
enum serial_notify{
SERIAL_WRITE_NOTIFY = 0x01,
SERIAL_READ_NOTIFY = 0x02,
};
/* ---------- LED Animation / Personal States ------------------------------ */
#define PERSONAL_STATE_LED 14
void vLedTask(void *pvParameters);
int personal_state_enabled();
/* ---------- PMIC --------------------------------------------------------- */
void vPmicTask(void *pvParameters);
/* ---------- Watchdog ----------------------------------------------------- */
void watchdog_init();
void watchdog_clearer_init();
/* Critical battery voltage */
#define BATTERY_CRITICAL 3.40f
enum pmic_amux_signal {
PMIC_AMUX_DISABLED = 0x0,
PMIC_AMUX_CHGIN_U = 0x1,
PMIC_AMUX_CHGIN_I = 0x2,
PMIC_AMUX_BATT_U = 0x3,
PMIC_AMUX_BATT_CHG_I = 0x4,
PMIC_AMUX_BATT_DIS_I = 0x5,
PMIC_AMUX_BATT_NULL_I = 0x6,
PMIC_AMUX_THM_U = 0x7,
PMIC_AMUX_TBIAS_U = 0x8,
PMIC_AMUX_AGND_U = 0x9,
PMIC_AMUX_SYS_U = 0xA,
_PMIC_AMUX_MAX,
};
/*
* Read a value from the PMIC's AMUX. The result is already converted into its
* proper unit. See the MAX77650 datasheet for details.
*/
int pmic_read_amux(enum pmic_amux_signal sig, float *result);
/* ---------- BLE ---------------------------------------------------------- */
void vBleTask(void *pvParameters);
bool ble_shall_start(void);
bool ble_is_enabled(void);
void ble_uart_write(uint8_t *pValue, uint8_t len);
/* ---------- Hardware (Peripheral) Locks ---------------------------------- */
......@@ -97,28 +35,8 @@ enum hwlock_periph {
_HWLOCK_MAX,
};
int hwlock_acquire(enum hwlock_periph p, TickType_t wait);
int hwlock_release(enum hwlock_periph p);
/* ---------- Display ------------------------------------------------------ */
/* Forces an unlock of the display. Only to be used in Epicardium */
void disp_forcelock();
/* ---------- BHI160 ------------------------------------------------------- */
#define BHI160_FIFO_SIZE 128
#define BHI160_MUTEX_WAIT_MS 50
void vBhi160Task(void *pvParameters);
/* ---------- MAX30001 ----------------------------------------------------- */
#define MAX30001_MUTEX_WAIT_MS 50
void vMAX30001Task(void *pvParameters);
void max30001_mutex_init(void);
/* ---------- GPIO --------------------------------------------------------- */
#define MAX30001_MUTEX_WAIT_MS 50
extern gpio_cfg_t gpio_configs[];
void hwlock_acquire(enum hwlock_periph p);
int hwlock_acquire_nonblock(enum hwlock_periph p);
void hwlock_release(enum hwlock_periph p);
/* ---------- Sleep -------------------------------------------------------- */
void sleep_deepsleep(void);
#endif /* MODULES_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 "modules/log.h"
#include "modules/modules.h"
#include "card10.h"
#include "card10-version.h"
#include <stdio.h>
#include <stdarg.h>
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?issue <-\n"
"\x1b[31m --- ====== ===== ---\x1b[0m\n");
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);
}
......@@ -2,23 +2,39 @@
#include "leds.h"
#include "modules.h"
#include "os/work_queue.h"
#include "FreeRTOS.h"
#include "timers.h"
#include <math.h>
uint8_t _personal_state_enabled = 0;
uint8_t personal_state = STATE_NONE;
uint8_t personal_state_persistent = 0;
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;
int led_animation_ticks = 0;
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_NONE || state > STATE_CAMP)
if (state > STATE_CAMP)
return -EINVAL;
led_animation_state = 0;
......@@ -30,16 +46,31 @@ int epic_personal_state_set(uint8_t state, bool persistent)
_personal_state_enabled = (state != STATE_NONE);
personal_state_persistent = persistent;
if (was_enabled && !_personal_state_enabled) {
while (hwlock_acquire(HWLOCK_LED, pdMS_TO_TICKS(1)) < 0) {
vTaskDelay(pdMS_TO_TICKS(1));
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.
}
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
leds_update_power();
leds_update();
if (xTimerIsTimerActive(led_timer) == pdFALSE) {
xTimerStart(led_timer, 0);
}
hwlock_release(HWLOCK_LED);
} 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;
......@@ -55,24 +86,15 @@ int epic_personal_state_is_persistent()
return personal_state_persistent;
}
void vLedTask(void *pvParameters)
static void worktick(void *data)
{
const int led_animation_rate = 1000 / 25; /* 25Hz -> 40ms*/
while (1) {
if (_personal_state_enabled) {
while (hwlock_acquire(HWLOCK_LED, pdMS_TO_TICKS(1)) <
0) {
vTaskDelay(pdMS_TO_TICKS(1));
}
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
);
leds_prep(PERSONAL_STATE_LED, 0, 0, 255);
if (led_animation_ticks >
(200 / led_animation_rate)) {
led_animation_ticks = 0;
......@@ -86,9 +108,7 @@ void vLedTask(void *pvParameters)
led_animation_state = 2;
}
} else if (led_animation_state == 2) {
leds_prep(
PERSONAL_STATE_LED, 0, 0, 255
);
leds_prep(PERSONAL_STATE_LED, 0, 0, 255);
if (led_animation_ticks >
(1000 / led_animation_rate)) {
led_animation_ticks = 0;
......@@ -104,9 +124,7 @@ void vLedTask(void *pvParameters)
}
} else if (personal_state == STATE_COMMUNICATION) {
if (led_animation_state == 0) {
leds_prep(
PERSONAL_STATE_LED, 255, 255, 0
);
leds_prep(PERSONAL_STATE_LED, 255, 255, 0);
if (led_animation_ticks >
(1000 / led_animation_rate)) {
led_animation_ticks = 0;
......@@ -127,15 +145,9 @@ void vLedTask(void *pvParameters)
1.0f,
fabs(sin(
led_animation_ticks /
(float)(1000 /
led_animation_rate))));
}
leds_update_power();
leds_update();
hwlock_release(HWLOCK_LED);
(float)(1000 / led_animation_rate))));
}
vTaskDelay(led_animation_rate / portTICK_PERIOD_MS);
epic_leds_update();
}
}
......@@ -4,9 +4,9 @@
#include "queue.h"
#include "epicardium.h"
#include "modules/log.h"
#include "os/core.h"
#include "modules/stream.h"
#include "modules/mutex.h"
#include "os/mutex.h"
/* Internal buffer of registered streams */
static struct stream_info *stream_table[SD_MAX];
......
......@@ -11,6 +11,7 @@
#include "queue.h"
#else
typedef unsigned int size_t;
typedef int bool;
typedef void *QueueHandle_t;
#endif /* __SPHINX_DOC */
......@@ -26,12 +27,17 @@ typedef void *QueueHandle_t;
* Please keep IDs in sequential order.
*/
enum stream_descriptor {
/** BHI160 */
/** 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,
};
......@@ -62,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 "epicardium.h"
#include "trng.h"
int epic_trng_read(uint8_t *dest, size_t size)
{
if (dest == NULL)
return -EFAULT;
TRNG_Read(MXC_TRNG, dest, size);
return 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',
)