2011-01-16 05:14:21 -04:00
|
|
|
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
2010-12-31 02:20:28 -04:00
|
|
|
//
|
|
|
|
// This is free software; you can redistribute it and/or modify it under
|
|
|
|
// the terms of the GNU Lesser General Public License as published by the
|
|
|
|
// Free Software Foundation; either version 2.1 of the License, or (at
|
|
|
|
// your option) any later version.
|
|
|
|
//
|
2011-01-23 19:58:07 -04:00
|
|
|
|
|
|
|
/// @file AP_Var.cpp
|
|
|
|
/// @brief The AP variable store.
|
2010-12-31 02:20:28 -04:00
|
|
|
|
2011-01-23 04:11:53 -04:00
|
|
|
#if 0
|
|
|
|
# include <FastSerial.h>
|
|
|
|
extern "C" { extern void delay(unsigned long); }
|
2011-02-09 19:15:11 -04:00
|
|
|
# define debug(fmt, args...) do {Serial.printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__ , ##args); delay(100); } while(0)
|
2011-01-23 04:11:53 -04:00
|
|
|
#else
|
2011-02-09 19:15:11 -04:00
|
|
|
# define debug(fmt, args...)
|
2011-01-23 04:11:53 -04:00
|
|
|
#endif
|
|
|
|
|
2011-01-23 19:58:07 -04:00
|
|
|
#include <AP_Common.h>
|
|
|
|
|
2011-02-08 06:17:45 -04:00
|
|
|
#include <math.h>
|
2011-01-23 19:58:07 -04:00
|
|
|
#include <string.h>
|
2010-12-31 02:20:28 -04:00
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Global constants exported for general use.
|
2011-01-05 05:25:07 -04:00
|
|
|
//
|
2011-01-23 02:52:59 -04:00
|
|
|
AP_Float AP_Float_unity ( 1.0, AP_Var::k_key_none, NULL, AP_Var::k_flag_unlisted);
|
|
|
|
AP_Float AP_Float_negative_unity(-1.0, AP_Var::k_key_none, NULL, AP_Var::k_flag_unlisted);
|
|
|
|
AP_Float AP_Float_zero ( 0.0, AP_Var::k_key_none, NULL, AP_Var::k_flag_unlisted);
|
2011-01-16 05:14:21 -04:00
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Static member variables for AP_Var.
|
2011-01-16 05:14:21 -04:00
|
|
|
//
|
2011-01-23 02:52:59 -04:00
|
|
|
AP_Var *AP_Var::_variables;
|
|
|
|
AP_Var *AP_Var::_grouped_variables;
|
|
|
|
uint16_t AP_Var::_tail_sentinel;
|
2011-02-16 03:01:17 -04:00
|
|
|
uint16_t AP_Var::_bytes_in_use;
|
2011-01-16 05:14:21 -04:00
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Constructor for standalone variables
|
2011-01-04 04:49:55 -04:00
|
|
|
//
|
2011-05-04 16:12:27 -03:00
|
|
|
AP_Var::AP_Var(Key p_key, const prog_char_t *name, Flags flags) :
|
2011-01-23 02:52:59 -04:00
|
|
|
_group(NULL),
|
2011-05-04 16:12:27 -03:00
|
|
|
_key(p_key | k_key_not_located),
|
2011-01-23 02:52:59 -04:00
|
|
|
_name(name),
|
|
|
|
_flags(flags)
|
|
|
|
{
|
|
|
|
// Insert the variable or group into the list of known variables, unless
|
|
|
|
// it wants to be unlisted.
|
|
|
|
//
|
|
|
|
if (!has_flags(k_flag_unlisted)) {
|
|
|
|
_link = _variables;
|
|
|
|
_variables = this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constructor for variables in a group
|
|
|
|
//
|
2011-05-04 16:12:27 -03:00
|
|
|
AP_Var::AP_Var(AP_Var_group *pGroup, Key index, const prog_char_t *name, Flags flags) :
|
|
|
|
_group(pGroup),
|
2011-01-23 02:52:59 -04:00
|
|
|
_key(index),
|
2011-01-22 04:37:52 -04:00
|
|
|
_name(name),
|
|
|
|
_flags(flags)
|
2011-01-04 04:49:55 -04:00
|
|
|
{
|
2011-01-23 02:52:59 -04:00
|
|
|
AP_Var **vp;
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Sort the variable into the list of group-member variables.
|
2011-01-22 04:37:52 -04:00
|
|
|
//
|
2011-01-23 02:52:59 -04:00
|
|
|
// This list is kept sorted so that groups can traverse forwards along
|
|
|
|
// it in order to enumerate their members in key order.
|
2011-01-22 04:37:52 -04:00
|
|
|
//
|
2011-01-23 02:52:59 -04:00
|
|
|
// We use a pointer-to-pointer insertion technique here; vp points
|
|
|
|
// to the pointer to the node that we are considering inserting in front of.
|
|
|
|
//
|
|
|
|
vp = &_grouped_variables;
|
|
|
|
while (*vp != NULL) {
|
2011-02-19 20:38:09 -04:00
|
|
|
if ((*vp)->_key >= _key) {
|
2011-01-23 02:52:59 -04:00
|
|
|
break;
|
2011-01-22 04:37:52 -04:00
|
|
|
}
|
2011-01-23 02:52:59 -04:00
|
|
|
vp = &((*vp)->_link);
|
2011-01-22 04:37:52 -04:00
|
|
|
}
|
2011-01-23 02:52:59 -04:00
|
|
|
_link = *vp;
|
|
|
|
*vp = this;
|
2011-01-04 04:49:55 -04:00
|
|
|
}
|
|
|
|
|
2011-01-02 22:28:35 -04:00
|
|
|
// Destructor
|
|
|
|
//
|
|
|
|
AP_Var::~AP_Var(void)
|
|
|
|
{
|
2011-01-22 04:37:52 -04:00
|
|
|
AP_Var **vp;
|
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Determine which list the variable may be in.
|
|
|
|
// If the variable is a group member and the group has already
|
|
|
|
// been destroyed, it may not be in any list.
|
2011-01-22 04:37:52 -04:00
|
|
|
//
|
2011-01-23 02:52:59 -04:00
|
|
|
if (_group) {
|
|
|
|
vp = &_grouped_variables;
|
|
|
|
} else {
|
|
|
|
vp = &_variables;
|
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Scan the list and remove this if we find it
|
|
|
|
while (*vp) {
|
2011-01-22 04:37:52 -04:00
|
|
|
if (*vp == this) {
|
|
|
|
*vp = _link;
|
|
|
|
break;
|
2011-01-16 05:14:21 -04:00
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
vp = &((*vp)->_link);
|
2011-01-16 05:14:21 -04:00
|
|
|
}
|
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// If we are destroying a group, remove all its variables from the list
|
|
|
|
//
|
|
|
|
if (has_flags(k_flag_is_group)) {
|
|
|
|
|
|
|
|
// Scan the list and remove any variable that has this as its group
|
|
|
|
vp = &_grouped_variables;
|
|
|
|
while (*vp) {
|
|
|
|
|
|
|
|
// Does the variable claim us as its group?
|
|
|
|
if ((*vp)->_group == this) {
|
|
|
|
*vp = (*vp)->_link;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
vp = &((*vp)->_link);
|
|
|
|
}
|
|
|
|
}
|
2011-01-05 05:25:07 -04:00
|
|
|
}
|
|
|
|
|
2011-01-22 04:37:52 -04:00
|
|
|
// Copy the variable's whole name to the supplied buffer.
|
|
|
|
//
|
|
|
|
// If the variable is a group member, prepend the group name.
|
2011-01-04 04:49:55 -04:00
|
|
|
//
|
2011-01-16 05:14:21 -04:00
|
|
|
void AP_Var::copy_name(char *buffer, size_t buffer_size) const
|
2011-01-04 04:49:55 -04:00
|
|
|
{
|
2011-01-16 05:14:21 -04:00
|
|
|
buffer[0] = '\0';
|
2011-02-14 13:44:33 -04:00
|
|
|
if (_name) {
|
|
|
|
if (_group)
|
|
|
|
_group->copy_name(buffer, buffer_size);
|
|
|
|
strlcat_P(buffer, _name, buffer_size);
|
|
|
|
}
|
2011-01-04 04:49:55 -04:00
|
|
|
}
|
|
|
|
|
2011-01-23 19:58:07 -04:00
|
|
|
// Find a variable by name.
|
|
|
|
//
|
|
|
|
AP_Var *
|
|
|
|
AP_Var::find(const char *name)
|
|
|
|
{
|
|
|
|
AP_Var *vp;
|
|
|
|
|
|
|
|
for (vp = first(); vp; vp = vp->next()) {
|
|
|
|
char name_buffer[32];
|
|
|
|
|
|
|
|
// copy the variable's name into our scratch buffer
|
|
|
|
vp->copy_name(name_buffer, sizeof(name_buffer));
|
|
|
|
|
|
|
|
// compare with the user-supplied name
|
|
|
|
if (!strcmp(name, name_buffer)) {
|
|
|
|
return vp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-03-24 23:46:45 -03:00
|
|
|
// Find a variable by key.
|
|
|
|
//
|
|
|
|
AP_Var *
|
|
|
|
AP_Var::find(Key key)
|
|
|
|
{
|
|
|
|
AP_Var *vp;
|
|
|
|
|
|
|
|
for (vp = first(); vp; vp = vp->next()) {
|
|
|
|
|
|
|
|
if (key == vp->key()) {
|
|
|
|
return vp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-01-23 19:58:07 -04:00
|
|
|
|
2011-01-22 04:37:52 -04:00
|
|
|
// Save the variable to EEPROM, if supported
|
2011-01-04 04:49:55 -04:00
|
|
|
//
|
2011-01-22 04:37:52 -04:00
|
|
|
bool AP_Var::save(void)
|
2011-01-04 04:49:55 -04:00
|
|
|
{
|
2011-01-23 02:52:59 -04:00
|
|
|
uint8_t vbuf[k_size_max];
|
2011-01-22 04:37:52 -04:00
|
|
|
size_t size;
|
2011-01-04 04:49:55 -04:00
|
|
|
|
2011-01-22 04:37:52 -04:00
|
|
|
// if the variable is a group member, save the group
|
|
|
|
if (_group) {
|
|
|
|
return _group->save();
|
2011-01-16 05:14:21 -04:00
|
|
|
}
|
2011-01-04 04:49:55 -04:00
|
|
|
|
2011-02-16 03:01:17 -04:00
|
|
|
debug("save: %S", _name ? _name : PSTR("??"));
|
|
|
|
|
2011-01-22 04:37:52 -04:00
|
|
|
// locate the variable in EEPROM, allocating space as required
|
|
|
|
if (!_EEPROM_locate(true)) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("locate failed");
|
2011-01-22 04:37:52 -04:00
|
|
|
return false;
|
2011-01-16 05:14:21 -04:00
|
|
|
}
|
2011-01-04 04:49:55 -04:00
|
|
|
|
2011-01-22 04:37:52 -04:00
|
|
|
// serialize the variable into the buffer and work out how big it is
|
|
|
|
size = serialize(vbuf, sizeof(vbuf));
|
2011-02-21 01:11:02 -04:00
|
|
|
if (0 == size) {
|
|
|
|
// variable cannot be serialised into the buffer
|
|
|
|
debug("cannot save (too big or not supported)");
|
|
|
|
return false;
|
|
|
|
}
|
2011-01-04 04:49:55 -04:00
|
|
|
|
2011-01-22 04:37:52 -04:00
|
|
|
// if it fit in the buffer, save it to EEPROM
|
|
|
|
if (size <= sizeof(vbuf)) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("saving %u to %u", size, _key);
|
2011-01-22 04:37:52 -04:00
|
|
|
eeprom_write_block(vbuf, (void *)_key, size);
|
2011-01-23 04:11:53 -04:00
|
|
|
return true;
|
2011-01-16 05:14:21 -04:00
|
|
|
}
|
2011-01-23 04:11:53 -04:00
|
|
|
return false;
|
2011-01-04 04:49:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load the variable from EEPROM, if supported
|
|
|
|
//
|
2011-01-22 04:37:52 -04:00
|
|
|
bool AP_Var::load(void)
|
2011-01-04 04:49:55 -04:00
|
|
|
{
|
2011-01-23 02:52:59 -04:00
|
|
|
uint8_t vbuf[k_size_max];
|
2011-01-22 04:37:52 -04:00
|
|
|
size_t size;
|
2011-01-04 04:49:55 -04:00
|
|
|
|
2011-01-22 04:37:52 -04:00
|
|
|
// if the variable is a group member, load the group
|
|
|
|
if (_group) {
|
|
|
|
return _group->load();
|
|
|
|
}
|
2011-01-04 04:49:55 -04:00
|
|
|
|
2011-02-16 03:01:17 -04:00
|
|
|
debug("load: %S", _name ? _name : PSTR("??"));
|
|
|
|
|
2011-01-22 04:37:52 -04:00
|
|
|
// locate the variable in EEPROM, but do not allocate space
|
|
|
|
if (!_EEPROM_locate(false)) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("locate failed");
|
2011-01-22 04:37:52 -04:00
|
|
|
return false;
|
|
|
|
}
|
2011-01-04 04:49:55 -04:00
|
|
|
|
2011-02-19 20:38:09 -04:00
|
|
|
// ask the serializer how big the variable is
|
2011-01-22 04:37:52 -04:00
|
|
|
//
|
|
|
|
// XXX should check size in EEPROM var header too...
|
|
|
|
//
|
2011-02-19 20:38:09 -04:00
|
|
|
size = serialize(NULL, 0);
|
2011-02-21 01:11:02 -04:00
|
|
|
if (0 == size) {
|
|
|
|
debug("cannot load (too big or not supported)");
|
|
|
|
return false;
|
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
|
|
|
// Read the buffer from EEPROM, now that _EEPROM_locate
|
|
|
|
// has converted _key into an EEPROM address.
|
|
|
|
//
|
|
|
|
if (size <= sizeof(vbuf)) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("loading %u from %u", size, _key);
|
2011-01-22 04:37:52 -04:00
|
|
|
eeprom_read_block(vbuf, (void *)_key, size);
|
2011-01-23 04:11:53 -04:00
|
|
|
return unserialize(vbuf, size);
|
2011-01-16 05:14:21 -04:00
|
|
|
}
|
2011-01-23 04:11:53 -04:00
|
|
|
return false;
|
2011-01-04 04:49:55 -04:00
|
|
|
}
|
|
|
|
|
2011-01-22 04:37:52 -04:00
|
|
|
// Save all variables that don't opt out.
|
|
|
|
//
|
2011-01-02 18:14:36 -04:00
|
|
|
//
|
2011-01-22 04:37:52 -04:00
|
|
|
bool AP_Var::save_all(void)
|
2011-01-02 18:14:36 -04:00
|
|
|
{
|
2011-01-22 04:37:52 -04:00
|
|
|
bool result = true;
|
2011-01-23 05:27:58 -04:00
|
|
|
AP_Var *vp = _variables;
|
2011-01-16 05:14:21 -04:00
|
|
|
|
2011-01-23 05:27:58 -04:00
|
|
|
while (vp) {
|
|
|
|
if (!vp->has_flags(k_flag_no_auto_load) && // not opted out of autosave
|
|
|
|
(vp->_key != k_key_none)) { // has a key
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 05:27:58 -04:00
|
|
|
if (!vp->save()) {
|
2011-01-22 04:37:52 -04:00
|
|
|
result = false;
|
|
|
|
}
|
2011-01-16 05:14:21 -04:00
|
|
|
}
|
2011-01-23 05:27:58 -04:00
|
|
|
vp = vp->_link;
|
2011-01-16 05:14:21 -04:00
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
return result;
|
2011-01-02 18:14:36 -04:00
|
|
|
}
|
|
|
|
|
2011-01-22 04:37:52 -04:00
|
|
|
// Load all variables that don't opt out.
|
2011-01-02 18:14:36 -04:00
|
|
|
//
|
2011-01-22 04:37:52 -04:00
|
|
|
bool AP_Var::load_all(void)
|
2011-01-02 18:14:36 -04:00
|
|
|
{
|
2011-01-23 05:27:58 -04:00
|
|
|
bool result = true;
|
|
|
|
AP_Var *vp = _variables;
|
2011-01-16 05:14:21 -04:00
|
|
|
|
2011-01-23 05:27:58 -04:00
|
|
|
while (vp) {
|
|
|
|
if (!vp->has_flags(k_flag_no_auto_load) && // not opted out of autoload
|
|
|
|
(vp->_key != k_key_none)) { // has a key
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 05:27:58 -04:00
|
|
|
if (!vp->load()) {
|
2011-01-22 04:37:52 -04:00
|
|
|
result = false;
|
|
|
|
}
|
|
|
|
}
|
2011-01-23 05:27:58 -04:00
|
|
|
vp = vp->_link;
|
2011-01-22 04:37:52 -04:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Erase all variables in EEPROM.
|
2011-01-22 04:37:52 -04:00
|
|
|
//
|
2011-01-23 04:11:53 -04:00
|
|
|
// We first walk the variable set and recover their key values
|
|
|
|
// from EEPROM, so that we have a chance of saving them later.
|
|
|
|
//
|
2011-01-23 02:52:59 -04:00
|
|
|
void
|
|
|
|
AP_Var::erase_all()
|
|
|
|
{
|
2011-01-23 04:11:53 -04:00
|
|
|
AP_Var *vp;
|
|
|
|
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("erase EEPROM");
|
2011-01-23 04:11:53 -04:00
|
|
|
|
|
|
|
// Scan the list of variables/groups, fetching their key values and
|
|
|
|
// reverting them to their not-located state.
|
|
|
|
//
|
|
|
|
vp = _variables;
|
|
|
|
while (vp) {
|
|
|
|
vp->_key = vp->key() | k_key_not_located;
|
|
|
|
vp = vp->_link;
|
|
|
|
}
|
2011-01-23 02:52:59 -04:00
|
|
|
|
|
|
|
// overwrite the first byte of the header, invalidating the EEPROM
|
|
|
|
//
|
2011-01-23 04:11:53 -04:00
|
|
|
eeprom_write_byte(0, 0xff);
|
|
|
|
|
|
|
|
// revert to ignorance about the state of the EEPROM
|
|
|
|
_tail_sentinel = 0;
|
2011-01-23 02:52:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return the key for a variable.
|
|
|
|
//
|
|
|
|
AP_Var::Key
|
|
|
|
AP_Var::key(void)
|
2011-01-22 04:37:52 -04:00
|
|
|
{
|
|
|
|
Var_header var_header;
|
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
if (_group) { // group members don't have keys
|
|
|
|
return k_key_none;
|
|
|
|
}
|
|
|
|
if (_key && k_key_not_located) { // if not located, key is in memory
|
|
|
|
return _key & k_key_mask;
|
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 04:11:53 -04:00
|
|
|
// Read key from EEPROM, note that _key points to the space
|
|
|
|
// allocated for storage; the header is immediately before.
|
|
|
|
//
|
|
|
|
eeprom_read_block(&var_header, (void *)(_key - sizeof(var_header)), sizeof(var_header));
|
2011-01-23 02:52:59 -04:00
|
|
|
return var_header.key;
|
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-02-08 06:17:45 -04:00
|
|
|
// Default implementation of cast_to_float, which always fails.
|
|
|
|
//
|
|
|
|
float
|
|
|
|
AP_Var::cast_to_float(void) const
|
|
|
|
{
|
|
|
|
return NAN;
|
|
|
|
}
|
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Return the next variable in the global list.
|
|
|
|
//
|
|
|
|
AP_Var *
|
|
|
|
AP_Var::next(void)
|
|
|
|
{
|
|
|
|
// If there is a variable after this one, return it.
|
|
|
|
//
|
|
|
|
if (_link)
|
|
|
|
return _link;
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// If we are at the end of the _variables list, _group will be NULL; in that
|
|
|
|
// case, move to the _grouped_variables list.
|
|
|
|
//
|
|
|
|
if (!_group) {
|
|
|
|
return _grouped_variables;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We must be at the end of the _grouped_variables list, nothing remains.
|
|
|
|
//
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Return the first variable that is a member of the group.
|
|
|
|
//
|
|
|
|
AP_Var *
|
|
|
|
AP_Var::first_member(AP_Var_group *group)
|
|
|
|
{
|
|
|
|
AP_Var **vp;
|
|
|
|
|
|
|
|
vp = &_grouped_variables;
|
|
|
|
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("seeking %p", group);
|
2011-01-23 02:52:59 -04:00
|
|
|
while (*vp) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("consider %p with %p", *vp, (*vp)->_group);
|
2011-01-23 02:52:59 -04:00
|
|
|
if ((*vp)->_group == group) {
|
|
|
|
return *vp;
|
2011-01-22 04:37:52 -04:00
|
|
|
}
|
2011-01-23 02:52:59 -04:00
|
|
|
vp = &((*vp)->_link);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Return the next variable that is a member of the same group.
|
|
|
|
AP_Var *
|
|
|
|
AP_Var::next_member()
|
|
|
|
{
|
|
|
|
AP_Var *vp;
|
|
|
|
|
|
|
|
vp = _link;
|
|
|
|
while (vp) {
|
|
|
|
if (vp->_group == _group) {
|
|
|
|
return vp;
|
|
|
|
}
|
|
|
|
vp = vp->_link;
|
2011-01-16 05:14:21 -04:00
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scan the EEPROM and assign addresses to all the variables that
|
|
|
|
// are known and found therein.
|
|
|
|
//
|
|
|
|
bool AP_Var::_EEPROM_scan(void)
|
|
|
|
{
|
|
|
|
struct EEPROM_header ee_header;
|
|
|
|
struct Var_header var_header;
|
|
|
|
AP_Var *vp;
|
2011-01-23 04:11:53 -04:00
|
|
|
uint16_t eeprom_address;
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 04:11:53 -04:00
|
|
|
// Assume that the EEPROM contents are invalid
|
2011-01-22 04:37:52 -04:00
|
|
|
_tail_sentinel = 0;
|
|
|
|
|
|
|
|
// read the header and validate
|
2011-01-23 04:11:53 -04:00
|
|
|
eeprom_address = 0;
|
|
|
|
eeprom_read_block(&ee_header, (void *)eeprom_address, sizeof(ee_header));
|
2011-01-22 04:37:52 -04:00
|
|
|
if ((ee_header.magic != k_EEPROM_magic) ||
|
2011-01-23 04:11:53 -04:00
|
|
|
(ee_header.revision != k_EEPROM_revision)) {
|
|
|
|
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("no header, magic 0x%x revision %u", ee_header.magic, ee_header.revision);
|
2011-01-22 04:37:52 -04:00
|
|
|
return false;
|
2011-01-23 04:11:53 -04:00
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
|
|
|
// scan the EEPROM
|
|
|
|
//
|
|
|
|
// Avoid trying to read a header when there isn't enough space left.
|
|
|
|
//
|
2011-01-23 04:11:53 -04:00
|
|
|
eeprom_address = sizeof(ee_header);
|
|
|
|
while (eeprom_address < (k_EEPROM_size - sizeof(var_header) - 1)) {
|
|
|
|
|
|
|
|
// Read a variable header
|
|
|
|
//
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("reading header from %u", eeprom_address);
|
2011-01-23 04:11:53 -04:00
|
|
|
eeprom_read_block(&var_header, (void *)eeprom_address, sizeof(var_header));
|
|
|
|
|
|
|
|
// If the header is for the sentinel, scanning is complete
|
|
|
|
//
|
|
|
|
if (var_header.key == k_key_sentinel) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("found tail sentinel");
|
2011-01-22 04:37:52 -04:00
|
|
|
break;
|
2011-01-23 04:11:53 -04:00
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 04:11:53 -04:00
|
|
|
// Sanity-check the variable header and abort if it looks bad
|
|
|
|
//
|
2011-01-22 04:37:52 -04:00
|
|
|
if (k_EEPROM_size <= (
|
2011-01-23 04:11:53 -04:00
|
|
|
eeprom_address + // current position
|
|
|
|
sizeof(var_header) + // header for this variable
|
2011-01-22 04:37:52 -04:00
|
|
|
var_header.size + 1 + // data for this variable
|
2011-01-23 04:11:53 -04:00
|
|
|
sizeof(var_header))) { // header for sentinel
|
|
|
|
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("header overruns EEPROM");
|
2011-01-23 04:11:53 -04:00
|
|
|
return false;
|
2011-01-23 02:52:59 -04:00
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
|
|
|
// look for a variable with this key
|
2011-01-23 02:52:59 -04:00
|
|
|
vp = _variables;
|
|
|
|
while (vp) {
|
|
|
|
if (vp->key() == var_header.key) {
|
|
|
|
// adjust the variable's key to point to this entry
|
2011-01-23 04:11:53 -04:00
|
|
|
vp->_key = eeprom_address + sizeof(var_header);
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("update %p with key %u -> %u", vp, var_header.key, vp->_key);
|
2011-01-23 02:52:59 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
vp = vp->_link;
|
2011-01-22 04:37:52 -04:00
|
|
|
}
|
2011-01-23 04:11:53 -04:00
|
|
|
if (!vp) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("key %u not claimed (already scanned or unknown)", var_header.key);
|
2011-01-23 04:11:53 -04:00
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
|
|
|
// move to the next variable header
|
2011-01-23 04:11:53 -04:00
|
|
|
eeprom_address += sizeof(var_header) + var_header.size + 1;
|
2011-01-22 04:37:52 -04:00
|
|
|
}
|
2011-01-23 02:52:59 -04:00
|
|
|
|
2011-01-23 05:56:58 -04:00
|
|
|
// Mark any variables that weren't assigned addresses as not-allocated,
|
|
|
|
// so that we don't waste time looking for them again later.
|
|
|
|
//
|
|
|
|
// Note that this isn't done when the header is not found on an empty EEPROM.
|
|
|
|
// The first variable written on an empty EEPROM falls out as soon as the
|
|
|
|
// header is not found. The second will scan and find one variable, then
|
|
|
|
// mark all the rest as not allocated.
|
|
|
|
//
|
|
|
|
vp = _variables;
|
|
|
|
while (vp) {
|
|
|
|
if (vp->_key & k_key_not_located) {
|
|
|
|
vp->_key |= k_key_not_allocated;
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("key %u not allocated", vp->key());
|
2011-01-23 05:56:58 -04:00
|
|
|
}
|
|
|
|
vp = vp->_link;
|
|
|
|
}
|
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Scanning is complete
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("scan done");
|
2011-01-23 04:11:53 -04:00
|
|
|
_tail_sentinel = eeprom_address;
|
2011-01-22 04:37:52 -04:00
|
|
|
return true;
|
2011-01-02 18:14:36 -04:00
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
|
|
|
// Locate a variable in EEPROM, allocating space if required.
|
|
|
|
//
|
|
|
|
bool AP_Var::_EEPROM_locate(bool allocate)
|
|
|
|
{
|
|
|
|
Var_header var_header;
|
2011-01-23 02:52:59 -04:00
|
|
|
Key new_location;
|
2011-01-22 04:37:52 -04:00
|
|
|
size_t size;
|
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Is it a group member, or does it have a no-location key?
|
|
|
|
//
|
|
|
|
if (_group || (_key == k_key_none)) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("not addressable");
|
2011-01-23 02:52:59 -04:00
|
|
|
return false; // it is/does, and thus it has no location
|
2011-01-22 04:37:52 -04:00
|
|
|
}
|
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Has the variable already been located?
|
|
|
|
//
|
|
|
|
if (!(_key & k_key_not_located)) {
|
|
|
|
return true; // it has
|
2011-01-22 04:37:52 -04:00
|
|
|
}
|
|
|
|
|
2011-01-23 05:56:58 -04:00
|
|
|
// We don't know where this variable belongs. If the variable isn't
|
|
|
|
// marked as already having been looked for and not found in EEPROM,
|
|
|
|
// try scanning to see if we can locate it.
|
2011-01-23 04:11:53 -04:00
|
|
|
//
|
2011-01-23 05:56:58 -04:00
|
|
|
if (!(_key & k_key_not_allocated)) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("need scan");
|
2011-01-23 05:56:58 -04:00
|
|
|
_EEPROM_scan();
|
2011-01-23 04:11:53 -04:00
|
|
|
|
2011-01-23 05:56:58 -04:00
|
|
|
// Has the variable now been located?
|
|
|
|
//
|
|
|
|
if (!(_key & k_key_not_located)) {
|
|
|
|
return true; // it has
|
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
}
|
2011-01-23 05:56:58 -04:00
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// If not located and not permitted to allocate, we have failed.
|
|
|
|
//
|
2011-01-23 04:11:53 -04:00
|
|
|
if (!allocate) {
|
2011-01-22 04:37:52 -04:00
|
|
|
return false;
|
|
|
|
}
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("needs allocation");
|
2011-01-22 04:37:52 -04:00
|
|
|
|
|
|
|
// Ask the serializer for the size of the thing we are allocating, and fail
|
2011-01-23 02:52:59 -04:00
|
|
|
// if it is too large or if it has no size, as we will not be able to allocate
|
|
|
|
// space for it.
|
|
|
|
//
|
2011-01-22 04:37:52 -04:00
|
|
|
size = serialize(NULL, 0);
|
2011-01-23 04:11:53 -04:00
|
|
|
if ((size == 0) || (size > k_size_max)) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("size %u out of bounds", size);
|
2011-01-22 04:37:52 -04:00
|
|
|
return false;
|
2011-01-23 04:11:53 -04:00
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
|
|
|
// Make sure there will be space in the EEPROM for the variable, its
|
2011-01-23 02:52:59 -04:00
|
|
|
// header and the new tail sentinel.
|
|
|
|
//
|
2011-01-23 04:11:53 -04:00
|
|
|
if ((_tail_sentinel + size + sizeof(Var_header) * 2) > k_EEPROM_size) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("no space in EEPROM");
|
2011-01-22 04:37:52 -04:00
|
|
|
return false;
|
2011-01-23 04:11:53 -04:00
|
|
|
}
|
2011-01-22 04:37:52 -04:00
|
|
|
|
|
|
|
// If there is no data in the EEPROM, write the header and move the
|
2011-01-23 02:52:59 -04:00
|
|
|
// sentinel.
|
|
|
|
//
|
2011-01-22 04:37:52 -04:00
|
|
|
if (0 == _tail_sentinel) {
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("writing header");
|
2011-01-22 04:37:52 -04:00
|
|
|
EEPROM_header ee_header;
|
|
|
|
|
|
|
|
ee_header.magic = k_EEPROM_magic;
|
|
|
|
ee_header.revision = k_EEPROM_revision;
|
|
|
|
ee_header.spare = 0;
|
|
|
|
|
2011-01-23 04:11:53 -04:00
|
|
|
eeprom_write_block(&ee_header, (void *)0, sizeof(ee_header));
|
2011-01-22 04:37:52 -04:00
|
|
|
|
|
|
|
_tail_sentinel = sizeof(ee_header);
|
|
|
|
}
|
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Save the location we are going to insert at, and compute the new
|
|
|
|
// tail sentinel location.
|
|
|
|
//
|
|
|
|
new_location = _tail_sentinel;
|
2011-01-23 05:56:58 -04:00
|
|
|
_tail_sentinel += sizeof(var_header) + size;
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("allocated %u/%u for key %u new sentinel %u", new_location, size, key(), _tail_sentinel);
|
2011-01-23 02:52:59 -04:00
|
|
|
|
|
|
|
// Write the new sentinel first. If we are interrupted during this operation
|
|
|
|
// the old sentinel will still correctly terminate the EEPROM image.
|
|
|
|
//
|
|
|
|
var_header.key = k_key_sentinel;
|
2011-01-22 04:37:52 -04:00
|
|
|
var_header.size = 0;
|
2011-01-23 02:52:59 -04:00
|
|
|
eeprom_write_block(&var_header, (void *)_tail_sentinel, sizeof(var_header));
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// Write the header for the block we have just located, claiming the EEPROM space.
|
|
|
|
//
|
|
|
|
var_header.key = key();
|
2011-01-22 04:37:52 -04:00
|
|
|
var_header.size = size - 1;
|
2011-01-23 05:56:58 -04:00
|
|
|
eeprom_write_block(&var_header, (void *)new_location, sizeof(var_header));
|
2011-01-22 04:37:52 -04:00
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
// We have successfully allocated space and thus located the variable.
|
2011-01-23 04:11:53 -04:00
|
|
|
// Update _key to point to the space allocated for it.
|
2011-01-23 02:52:59 -04:00
|
|
|
//
|
2011-01-23 04:11:53 -04:00
|
|
|
_key = new_location + sizeof(var_header);
|
2011-01-22 04:37:52 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-01-22 14:42:44 -04:00
|
|
|
size_t
|
2011-01-23 05:27:58 -04:00
|
|
|
AP_Var_group::serialize(void *buf, size_t buf_size) const
|
2011-01-22 14:42:44 -04:00
|
|
|
{
|
2011-01-23 05:27:58 -04:00
|
|
|
// We have to cast away the const in order to call _serialize_unserialize,
|
|
|
|
// as it cannot be const due to changing this when called to unserialize.
|
|
|
|
//
|
2011-01-23 06:04:24 -04:00
|
|
|
// XXX it's questionable how much advantage we get from having ::serialize
|
|
|
|
// const in the first place...
|
|
|
|
//
|
2011-01-23 05:27:58 -04:00
|
|
|
return const_cast<AP_Var_group *>(this)->_serialize_unserialize(buf, buf_size, true);
|
2011-01-22 14:42:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
AP_Var_group::unserialize(void *buf, size_t buf_size)
|
|
|
|
{
|
|
|
|
return _serialize_unserialize(buf, buf_size, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
AP_Var_group::_serialize_unserialize(void *buf, size_t buf_size, bool do_serialize)
|
|
|
|
{
|
|
|
|
AP_Var *vp;
|
2011-01-23 06:04:24 -04:00
|
|
|
size_t size, total_size;
|
2011-01-22 14:42:44 -04:00
|
|
|
|
|
|
|
// Traverse the list of group members, serializing each in order
|
|
|
|
//
|
2011-01-23 02:52:59 -04:00
|
|
|
vp = first_member(this);
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("starting with %p", vp);
|
2011-01-22 14:42:44 -04:00
|
|
|
total_size = 0;
|
2011-01-23 02:52:59 -04:00
|
|
|
while (vp) {
|
2011-01-22 14:42:44 -04:00
|
|
|
|
|
|
|
// (un)serialise the group member
|
|
|
|
if (do_serialize) {
|
2011-01-23 06:04:24 -04:00
|
|
|
size = vp->serialize(buf, buf_size);
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("serialize %p -> %u", vp, size);
|
2011-01-22 14:42:44 -04:00
|
|
|
} else {
|
2011-01-23 06:04:24 -04:00
|
|
|
size = vp->unserialize(buf, buf_size);
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("unserialize %p -> %u", vp, size);
|
2011-01-22 14:42:44 -04:00
|
|
|
}
|
|
|
|
|
2011-02-21 01:11:02 -04:00
|
|
|
// Unserialize will return zero if the buffer is too small
|
|
|
|
// Serialize will only return zero if the variable cannot be serialised
|
|
|
|
// Either case is fatal for any operation we might be trying.
|
|
|
|
//
|
|
|
|
if (0 == size) {
|
|
|
|
debug("group (un)serialize failed, buffer too small or not supported");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-19 20:38:09 -04:00
|
|
|
|
2011-01-22 14:42:44 -04:00
|
|
|
// Account for the space that this variable consumes in the buffer
|
|
|
|
//
|
|
|
|
// We always count the total size, and we always advance the buffer pointer
|
|
|
|
// if there was room for the variable. This does mean that in the case where
|
|
|
|
// the buffer was too small for a variable in the middle of the group, that
|
|
|
|
// a smaller variable after it in the group may still be serialised into
|
|
|
|
// the buffer. Since that's a rare case it's not worth optimising for - in
|
|
|
|
// either case this function will return a size greater than the buffer size
|
|
|
|
// and the calling function will have to treat it as an error.
|
|
|
|
//
|
|
|
|
total_size += size;
|
2011-02-09 19:15:11 -04:00
|
|
|
debug("used %u", total_size);
|
2011-01-23 06:04:24 -04:00
|
|
|
if (size <= buf_size) {
|
2011-01-22 14:42:44 -04:00
|
|
|
// there was space for this one, account for it
|
2011-01-23 06:04:24 -04:00
|
|
|
buf_size -= size;
|
|
|
|
buf = (void *)((uint8_t *)buf + size);
|
2011-01-22 14:42:44 -04:00
|
|
|
}
|
|
|
|
|
2011-01-23 02:52:59 -04:00
|
|
|
vp = vp->next_member();
|
2011-01-22 14:42:44 -04:00
|
|
|
}
|
|
|
|
return total_size;
|
|
|
|
}
|
2011-01-23 19:58:07 -04:00
|
|
|
|
|
|
|
// Static pseudo-constant type IDs for known AP_VarT subclasses.
|
|
|
|
//
|
|
|
|
AP_Meta_class::Type_id AP_Var::k_typeid_float; ///< meta_type_id() value for AP_Float
|
|
|
|
AP_Meta_class::Type_id AP_Var::k_typeid_float16; ///< meta_type_id() value for AP_Float16
|
|
|
|
AP_Meta_class::Type_id AP_Var::k_typeid_int32; ///< meta_type_id() value for AP_Int32
|
|
|
|
AP_Meta_class::Type_id AP_Var::k_typeid_int16; ///< meta_type_id() value for AP_Int16
|
|
|
|
AP_Meta_class::Type_id AP_Var::k_typeid_int8; ///< meta_type_id() value for AP_Int8
|
2011-02-14 03:11:42 -04:00
|
|
|
AP_Meta_class::Type_id AP_Var::k_typeid_group; ///< meta_type_id() value for AP_Var_group
|
2011-01-23 19:58:07 -04:00
|
|
|
|
|
|
|
/// A special class used to initialise the k_typeid_* values that AP_Var exports.
|
|
|
|
///
|
|
|
|
class AP_Var_typesetup
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
/// Constructor
|
|
|
|
///
|
|
|
|
/// This constructor should be run just once by creating a static instance
|
|
|
|
/// of the class. It will initialise the k_typeid_* values for the well-known
|
|
|
|
/// AP_VarT subclasses.
|
|
|
|
///
|
|
|
|
/// When a new subclass is created, a new k_typeid_* constant should also be
|
|
|
|
/// created and the list below should likewise be expanded.
|
|
|
|
///
|
|
|
|
AP_Var_typesetup(void);
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Initialise AP_Var's k_typeid_* values
|
|
|
|
AP_Var_typesetup::AP_Var_typesetup(void)
|
|
|
|
{
|
|
|
|
AP_Var::k_typeid_float = AP_Meta_class::meta_type_id<AP_Float>();
|
|
|
|
AP_Var::k_typeid_float16 = AP_Meta_class::meta_type_id<AP_Float16>();
|
|
|
|
AP_Var::k_typeid_int32 = AP_Meta_class::meta_type_id<AP_Int32>();
|
|
|
|
AP_Var::k_typeid_int16 = AP_Meta_class::meta_type_id<AP_Int16>();
|
|
|
|
AP_Var::k_typeid_int8 = AP_Meta_class::meta_type_id<AP_Int8>();
|
2011-02-14 03:11:42 -04:00
|
|
|
AP_Var::k_typeid_group = AP_Meta_class::meta_type_id<AP_Var_group>();
|
2011-01-23 19:58:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Cause the AP_Var_typesetup constructor to be run.
|
|
|
|
///
|
|
|
|
static AP_Var_typesetup _typesetup __attribute__((used));
|