2016-05-25 18:37:40 +03:00
|
|
|
// Basic infrastructure commands.
|
|
|
|
//
|
2020-02-15 06:39:51 +03:00
|
|
|
// Copyright (C) 2016-2020 Kevin O'Connor <kevin@koconnor.net>
|
2016-05-25 18:37:40 +03:00
|
|
|
//
|
|
|
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
|
2017-07-20 06:10:08 +03:00
|
|
|
#include <string.h> // memset
|
2017-03-09 21:49:03 +03:00
|
|
|
#include "basecmd.h" // oid_lookup
|
2016-05-25 18:37:40 +03:00
|
|
|
#include "board/irq.h" // irq_save
|
|
|
|
#include "board/misc.h" // alloc_maxsize
|
2017-05-26 15:34:31 +03:00
|
|
|
#include "board/pgm.h" // READP
|
2016-05-25 18:37:40 +03:00
|
|
|
#include "command.h" // DECL_COMMAND
|
|
|
|
#include "sched.h" // sched_clear_shutdown
|
|
|
|
|
|
|
|
|
2017-07-20 06:10:08 +03:00
|
|
|
/****************************************************************
|
|
|
|
* Low level allocation
|
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
static void *alloc_end;
|
|
|
|
|
|
|
|
void
|
|
|
|
alloc_init(void)
|
|
|
|
{
|
|
|
|
alloc_end = (void*)ALIGN((size_t)dynmem_start(), __alignof__(void*));
|
|
|
|
}
|
|
|
|
DECL_INIT(alloc_init);
|
|
|
|
|
|
|
|
// Allocate an area of memory
|
2019-08-06 18:44:29 +03:00
|
|
|
void *
|
2017-07-20 06:10:08 +03:00
|
|
|
alloc_chunk(size_t size)
|
|
|
|
{
|
|
|
|
if (alloc_end + size > dynmem_end())
|
|
|
|
shutdown("alloc_chunk failed");
|
|
|
|
void *data = alloc_end;
|
|
|
|
alloc_end += ALIGN(size, __alignof__(void*));
|
|
|
|
memset(data, 0, size);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate an array of chunks
|
|
|
|
static void *
|
|
|
|
alloc_chunks(size_t size, size_t count, uint16_t *avail)
|
|
|
|
{
|
|
|
|
size_t can_alloc = 0;
|
|
|
|
void *p = alloc_end, *end = dynmem_end();
|
|
|
|
while (can_alloc < count && p + size <= end)
|
|
|
|
can_alloc++, p += size;
|
|
|
|
if (!can_alloc)
|
|
|
|
shutdown("alloc_chunks failed");
|
|
|
|
void *data = alloc_chunk(p - alloc_end);
|
|
|
|
*avail = can_alloc;
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-25 18:37:40 +03:00
|
|
|
/****************************************************************
|
|
|
|
* Move queue
|
|
|
|
****************************************************************/
|
|
|
|
|
2017-03-08 19:29:27 +03:00
|
|
|
struct move_freed {
|
|
|
|
struct move_freed *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct move_freed *move_free_list;
|
|
|
|
static void *move_list;
|
2016-05-25 18:37:40 +03:00
|
|
|
static uint16_t move_count;
|
2017-03-08 19:29:27 +03:00
|
|
|
static uint8_t move_item_size;
|
|
|
|
|
|
|
|
// Is the config and move queue finalized?
|
|
|
|
static int
|
|
|
|
is_finalized(void)
|
|
|
|
{
|
|
|
|
return !!move_count;
|
|
|
|
}
|
2016-05-25 18:37:40 +03:00
|
|
|
|
2017-03-08 19:29:27 +03:00
|
|
|
// Free previously allocated storage from move_alloc(). Caller must
|
|
|
|
// disable irqs.
|
2016-05-25 18:37:40 +03:00
|
|
|
void
|
2017-03-08 19:29:27 +03:00
|
|
|
move_free(void *m)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2017-03-08 19:29:27 +03:00
|
|
|
struct move_freed *mf = m;
|
|
|
|
mf->next = move_free_list;
|
|
|
|
move_free_list = mf;
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
|
2017-03-08 19:29:27 +03:00
|
|
|
// Allocate runtime storage
|
|
|
|
void *
|
2016-05-25 18:37:40 +03:00
|
|
|
move_alloc(void)
|
|
|
|
{
|
2016-06-09 04:13:53 +03:00
|
|
|
irqstatus_t flag = irq_save();
|
2017-03-08 19:29:27 +03:00
|
|
|
struct move_freed *mf = move_free_list;
|
|
|
|
if (!mf)
|
2016-05-25 18:37:40 +03:00
|
|
|
shutdown("Move queue empty");
|
2017-03-08 19:29:27 +03:00
|
|
|
move_free_list = mf->next;
|
2016-05-25 18:37:40 +03:00
|
|
|
irq_restore(flag);
|
2017-03-08 19:29:27 +03:00
|
|
|
return mf;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Request minimum size of runtime allocations returned by move_alloc()
|
|
|
|
void
|
|
|
|
move_request_size(int size)
|
|
|
|
{
|
|
|
|
if (size > UINT8_MAX || is_finalized())
|
|
|
|
shutdown("Invalid move request size");
|
|
|
|
if (size > move_item_size)
|
|
|
|
move_item_size = size;
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
|
2017-05-26 16:14:26 +03:00
|
|
|
void
|
2016-05-25 18:37:40 +03:00
|
|
|
move_reset(void)
|
|
|
|
{
|
|
|
|
if (!move_count)
|
|
|
|
return;
|
|
|
|
// Add everything in move_list to the free list.
|
|
|
|
uint32_t i;
|
2017-03-08 19:29:27 +03:00
|
|
|
for (i=0; i<move_count-1; i++) {
|
|
|
|
struct move_freed *mf = move_list + i*move_item_size;
|
|
|
|
mf->next = move_list + (i + 1)*move_item_size;
|
|
|
|
}
|
|
|
|
struct move_freed *mf = move_list + (move_count - 1)*move_item_size;
|
|
|
|
mf->next = NULL;
|
|
|
|
move_free_list = move_list;
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
DECL_SHUTDOWN(move_reset);
|
|
|
|
|
2017-03-08 19:29:27 +03:00
|
|
|
static void
|
|
|
|
move_finalize(void)
|
|
|
|
{
|
2017-07-20 06:10:08 +03:00
|
|
|
if (is_finalized())
|
|
|
|
shutdown("Already finalized");
|
2017-03-08 19:29:27 +03:00
|
|
|
move_request_size(sizeof(*move_free_list));
|
2017-07-20 06:10:08 +03:00
|
|
|
move_list = alloc_chunks(move_item_size, 1024, &move_count);
|
2017-03-08 19:29:27 +03:00
|
|
|
move_reset();
|
|
|
|
}
|
|
|
|
|
2016-05-25 18:37:40 +03:00
|
|
|
|
|
|
|
/****************************************************************
|
|
|
|
* Generic object ids (oid)
|
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
struct oid_s {
|
|
|
|
void *type, *data;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct oid_s *oids;
|
2017-03-09 21:49:03 +03:00
|
|
|
static uint8_t oid_count;
|
2016-05-25 18:37:40 +03:00
|
|
|
|
|
|
|
void *
|
2017-03-09 21:49:03 +03:00
|
|
|
oid_lookup(uint8_t oid, void *type)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2017-03-09 21:49:03 +03:00
|
|
|
if (oid >= oid_count || type != oids[oid].type)
|
2016-05-25 18:37:40 +03:00
|
|
|
shutdown("Invalid oid type");
|
|
|
|
return oids[oid].data;
|
|
|
|
}
|
|
|
|
|
2017-05-07 05:29:08 +03:00
|
|
|
void *
|
|
|
|
oid_alloc(uint8_t oid, void *type, uint16_t size)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2017-03-09 21:49:03 +03:00
|
|
|
if (oid >= oid_count || oids[oid].type || is_finalized())
|
2016-05-25 18:37:40 +03:00
|
|
|
shutdown("Can't assign oid");
|
|
|
|
oids[oid].type = type;
|
2017-05-07 05:29:08 +03:00
|
|
|
void *data = alloc_chunk(size);
|
2016-05-25 18:37:40 +03:00
|
|
|
oids[oid].data = data;
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
2017-03-09 21:49:03 +03:00
|
|
|
oid_next(uint8_t *i, void *type)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
|
|
|
uint8_t oid = *i;
|
|
|
|
for (;;) {
|
|
|
|
oid++;
|
2017-03-09 21:49:03 +03:00
|
|
|
if (oid >= oid_count)
|
2016-05-25 18:37:40 +03:00
|
|
|
return NULL;
|
|
|
|
if (oids[oid].type == type) {
|
|
|
|
*i = oid;
|
|
|
|
return oids[oid].data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
command_allocate_oids(uint32_t *args)
|
|
|
|
{
|
|
|
|
if (oids)
|
|
|
|
shutdown("oids already allocated");
|
|
|
|
uint8_t count = args[0];
|
2017-05-07 05:29:08 +03:00
|
|
|
oids = alloc_chunk(sizeof(oids[0]) * count);
|
2017-03-09 21:49:03 +03:00
|
|
|
oid_count = count;
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
DECL_COMMAND(command_allocate_oids, "allocate_oids count=%c");
|
|
|
|
|
2017-03-08 19:29:27 +03:00
|
|
|
|
|
|
|
/****************************************************************
|
|
|
|
* Config CRC
|
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
static uint32_t config_crc;
|
|
|
|
|
2016-05-25 18:37:40 +03:00
|
|
|
void
|
|
|
|
command_get_config(uint32_t *args)
|
|
|
|
{
|
2018-05-28 18:44:42 +03:00
|
|
|
sendf("config is_config=%c crc=%u move_count=%hu is_shutdown=%c"
|
|
|
|
, is_finalized(), config_crc, move_count, sched_is_shutdown());
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
DECL_COMMAND_FLAGS(command_get_config, HF_IN_SHUTDOWN, "get_config");
|
|
|
|
|
|
|
|
void
|
|
|
|
command_finalize_config(uint32_t *args)
|
|
|
|
{
|
2017-03-08 19:29:27 +03:00
|
|
|
move_finalize();
|
2016-05-25 18:37:40 +03:00
|
|
|
config_crc = args[0];
|
|
|
|
}
|
|
|
|
DECL_COMMAND(command_finalize_config, "finalize_config crc=%u");
|
|
|
|
|
2017-09-04 05:02:48 +03:00
|
|
|
// Attempt a full manual reset of the config
|
|
|
|
void
|
|
|
|
config_reset(uint32_t *args)
|
|
|
|
{
|
|
|
|
if (! sched_is_shutdown())
|
|
|
|
shutdown("config_reset only available when shutdown");
|
|
|
|
irq_disable();
|
|
|
|
config_crc = 0;
|
|
|
|
oid_count = 0;
|
|
|
|
oids = NULL;
|
|
|
|
move_free_list = NULL;
|
|
|
|
move_list = NULL;
|
|
|
|
move_count = move_item_size = 0;
|
|
|
|
alloc_init();
|
|
|
|
sched_timer_reset();
|
|
|
|
sched_clear_shutdown();
|
|
|
|
irq_enable();
|
|
|
|
}
|
|
|
|
|
2016-05-25 18:37:40 +03:00
|
|
|
|
|
|
|
/****************************************************************
|
|
|
|
* Timing and load stats
|
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
void
|
2018-05-28 18:38:04 +03:00
|
|
|
command_get_clock(uint32_t *args)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2018-05-28 18:38:04 +03:00
|
|
|
sendf("clock clock=%u", timer_read_time());
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
2018-05-28 18:38:04 +03:00
|
|
|
DECL_COMMAND_FLAGS(command_get_clock, HF_IN_SHUTDOWN, "get_clock");
|
2016-05-25 18:37:40 +03:00
|
|
|
|
2017-03-04 06:02:27 +03:00
|
|
|
static uint32_t stats_send_time, stats_send_time_high;
|
|
|
|
|
|
|
|
void
|
|
|
|
command_get_uptime(uint32_t *args)
|
|
|
|
{
|
2017-03-25 06:01:08 +03:00
|
|
|
uint32_t cur = timer_read_time();
|
2017-03-04 06:02:27 +03:00
|
|
|
uint32_t high = stats_send_time_high + (cur < stats_send_time);
|
|
|
|
sendf("uptime high=%u clock=%u", high, cur);
|
|
|
|
}
|
|
|
|
DECL_COMMAND_FLAGS(command_get_uptime, HF_IN_SHUTDOWN, "get_uptime");
|
|
|
|
|
2017-01-10 07:08:23 +03:00
|
|
|
#define SUMSQ_BASE 256
|
2019-03-05 07:35:11 +03:00
|
|
|
DECL_CONSTANT("STATS_SUMSQ_BASE", SUMSQ_BASE);
|
2017-01-10 07:08:23 +03:00
|
|
|
|
2017-05-26 16:14:26 +03:00
|
|
|
void
|
2017-08-07 19:33:08 +03:00
|
|
|
stats_update(uint32_t start, uint32_t cur)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2017-08-07 19:33:08 +03:00
|
|
|
static uint32_t count, sum, sumsq;
|
|
|
|
uint32_t diff = cur - start;
|
2016-05-25 18:37:40 +03:00
|
|
|
count++;
|
2017-08-07 19:33:08 +03:00
|
|
|
sum += diff;
|
2017-01-10 07:08:23 +03:00
|
|
|
// Calculate sum of diff^2 - be careful of integer overflow
|
|
|
|
uint32_t nextsumsq;
|
|
|
|
if (diff <= 0xffff) {
|
|
|
|
nextsumsq = sumsq + DIV_ROUND_UP(diff * diff, SUMSQ_BASE);
|
|
|
|
} else if (diff <= 0xfffff) {
|
|
|
|
nextsumsq = sumsq + DIV_ROUND_UP(diff, SUMSQ_BASE) * diff;
|
|
|
|
} else {
|
|
|
|
nextsumsq = 0xffffffff;
|
|
|
|
}
|
2016-05-25 18:37:40 +03:00
|
|
|
if (nextsumsq < sumsq)
|
|
|
|
nextsumsq = 0xffffffff;
|
|
|
|
sumsq = nextsumsq;
|
|
|
|
|
2017-03-25 06:01:08 +03:00
|
|
|
if (timer_is_before(cur, stats_send_time + timer_from_us(5000000)))
|
2016-05-25 18:37:40 +03:00
|
|
|
return;
|
2017-08-07 19:33:08 +03:00
|
|
|
sendf("stats count=%u sum=%u sumsq=%u", count, sum, sumsq);
|
2017-03-04 06:02:27 +03:00
|
|
|
if (cur < stats_send_time)
|
|
|
|
stats_send_time_high++;
|
|
|
|
stats_send_time = cur;
|
2017-08-07 19:33:08 +03:00
|
|
|
count = sum = sumsq = 0;
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************
|
|
|
|
* Misc commands
|
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
void
|
|
|
|
command_emergency_stop(uint32_t *args)
|
|
|
|
{
|
2017-09-06 04:15:24 +03:00
|
|
|
shutdown("Command request");
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
DECL_COMMAND_FLAGS(command_emergency_stop, HF_IN_SHUTDOWN, "emergency_stop");
|
|
|
|
|
|
|
|
void
|
|
|
|
command_clear_shutdown(uint32_t *args)
|
|
|
|
{
|
|
|
|
sched_clear_shutdown();
|
|
|
|
}
|
|
|
|
DECL_COMMAND_FLAGS(command_clear_shutdown, HF_IN_SHUTDOWN, "clear_shutdown");
|
|
|
|
|
|
|
|
void
|
|
|
|
command_identify(uint32_t *args)
|
|
|
|
{
|
|
|
|
uint32_t offset = args[0];
|
|
|
|
uint8_t count = args[1];
|
|
|
|
uint32_t isize = READP(command_identify_size);
|
|
|
|
if (offset >= isize)
|
|
|
|
count = 0;
|
|
|
|
else if (offset + count > isize)
|
|
|
|
count = isize - offset;
|
|
|
|
sendf("identify_response offset=%u data=%.*s"
|
|
|
|
, offset, count, &command_identify_data[offset]);
|
|
|
|
}
|
|
|
|
DECL_COMMAND_FLAGS(command_identify, HF_IN_SHUTDOWN,
|
|
|
|
"identify offset=%u count=%c");
|