1
0
Files
irix-657m-src/stand/arcs/lib/libkl/ml/promlog.c
2022-09-29 17:59:04 +03:00

1263 lines
25 KiB
C

/***********************************************************************\
* File: promlog.c *
* *
* Implements environment variables and logs in certain types *
* of flash PROMs such as the AM29F080. These FPROMs allow two *
* operations: clearing any individual bit from 1 to 0, or *
* setting an entire sector of the PROM to all 1s. *
* *
* This module is layered on top of the fprom module. *
* *
* NOTE: Please keep in sync with kernel version in *
* kern/ml/SN/promlog.c *
* *
\***********************************************************************/
#ident "$Revision: 1.13 $"
#include <sys/types.h>
#include <sys/SN/promlog.h>
#ifdef _STANDALONE
#ifdef EMULATE
#include <stdio.h>
#else
#include "libc.h"
#endif
#else
#include <sys/systm.h>
#endif
#define LDEBUG 0
#define TEST_SHORT 0
#if LDEBUG
static int debug_mode = 1;
static void LDUMP(char *s, promlog_t *l)
{
if (debug_mode)
printf("%s: act=%d ls=0x%x le=0x%x as=0x%x ae=0x%x pos=0x%x\n",
s, l->active,
l->log_start, l->log_end,
l->alt_start, l->alt_end, l->pos);
}
static void EDUMP(char *s, promlog_ent_t *e)
{
if (debug_mode)
printf("%s: v=%d type=%d key_len=%d cpu=%d value_len=%d\n",
s, e->valid, e->type, e->key_len, e->cpu_num, e->value_len);
}
static void HDUMP(char *s, promlog_header_t *h)
{
if (debug_mode)
printf("%s: magic=%x version=%d sequence=%d\n",
s, h->magic, h->version, h->sequence);
}
#define PRINT(a) if (debug_mode) printf a
#else /* LDEBUG */
#define LDUMP(s, l)
#define EDUMP(s, e)
#define HDUMP(s, h)
#define PRINT(a)
#endif /* LDEBUG */
/*
* get_entry (INTERNAL)
*/
static int get_entry(promlog_t *l)
{
int r;
LDUMP("get_entry", l);
if ((l->pos < l->alt_start || l->pos >= l->alt_end) &&
(l->pos < l->log_start || l->pos >= l->log_end)) {
r = PROMLOG_ERROR_CORRUPT;
goto done;
}
if ((r = fprom_read(&l->f, l->pos,
(char *) &l->ent, sizeof (l->ent))) < 0) {
printf("promlog: Error reading entry at 0x%x: %s\n",
l->pos, fprom_errmsg(r));
r = PROMLOG_ERROR_PROM;
goto done;
}
EDUMP("get_entry", &l->ent);
r = 0;
done:
PRINT(("get_entry: r=%d\n", r));
return r;
}
/*
* get_header (INTERNAL)
*/
static int get_header(promlog_t *l, int which, promlog_header_t *h)
{
int r;
LDUMP("get_header", l);
PRINT(("get_header: which=%d\n", which));
if ((r = fprom_read(&l->f, (l->sector_base + which) * FPROM_SECTOR_SIZE,
(char *) h, sizeof (*h))) < 0) {
printf("promlog: Error reading header %d: %s\n",
which, fprom_errmsg(r));
r = PROMLOG_ERROR_PROM;
goto done;
}
HDUMP("get_header", h);
r = 0;
done:
PRINT(("get_header: r=%d\n", r));
return r;
}
/*
* put_header (INTERNAL)
*/
static int put_header(promlog_t *l, int which, promlog_header_t *h)
{
int r;
LDUMP("put_header", l);
PRINT(("put_header: which=%d\n", which));
HDUMP("put_header", h);
if ((r = fprom_write(&l->f, (l->sector_base + which) * FPROM_SECTOR_SIZE,
(char *) h, sizeof (*h))) < 0) {
printf("promlog: Error writing header %d: %s\n",
which, fprom_errmsg(r));
r = PROMLOG_ERROR_PROM;
goto done;
}
r = 0;
done:
PRINT(("put_header: r=%d\n", r));
return r;
}
/*
* swap_log_alt (INTERNAL)
*/
static void swap_log_alt(promlog_t *l)
{
int i;
i = l->log_start;
l->log_start = l->alt_start;
l->alt_start = i;
i = l->log_end;
l->log_end = l->alt_end;
l->alt_end = i;
l->active = 1 - l->active;
PRINT(("swap_log_alt: active now %d\n", l->active));
}
/*
* set_ranges (INTERNAL)
*/
static void set_ranges(promlog_t *l)
{
PRINT(("set_ranges\n"));
l->log_start = (l->sector_base * FPROM_SECTOR_SIZE +
PROMLOG_OFFSET_ENTRY0);
l->log_end = (l->sector_base + 1) * FPROM_SECTOR_SIZE;
l->alt_start = ((l->sector_base + 1) * FPROM_SECTOR_SIZE +
PROMLOG_OFFSET_ENTRY0);
l->alt_end = (l->sector_base + 2) * FPROM_SECTOR_SIZE;
#if TEST_SHORT
l->log_end = l->log_start + 0x40;
l->alt_end = l->alt_start + 0x40;
#endif /* TEST_SHORT */
}
/*
* promlog_init
*
* Initializes the promlog_t handle for accessing a specified prom
* log. Identifies the active sector and sets the current position
* to the first log entry (or end marker if log is empty).
*
* Fprom_base is a pointer to the base of the PROM containing the
* log. Sector_base is the first sector in the PROM to use
* (typically 14). Fprom_type is FPROM_DEV_HUB or FPROM_DEV_IO6.
*/
int promlog_init(promlog_t *l, fprom_t *f,
int sector_base, int cpu_num)
{
promlog_header_t h1, h2;
int r;
#if defined (_STANDALONE)
memcpy(&l->f, f, sizeof (*f));
#else /* _STANDALONE */
bcopy (f, &l->f, sizeof (*f));
#endif /* _STANDALONE */
l->sector_base = sector_base;
l->cpu_num = cpu_num;
l->active = 0;
set_ranges(l);
LDUMP("promlog_init", l);
if ((r = get_header(l, 0, &h1)) < 0 ||
(r = get_header(l, 1, &h2)) < 0)
goto done;
/*
* Determine the active sector.
*/
if (h1.magic != PROMLOG_MAGIC && h2.magic != PROMLOG_MAGIC) {
r = PROMLOG_ERROR_MAGIC;
goto done;
}
if (h1.magic != PROMLOG_MAGIC ||
h2.magic == PROMLOG_MAGIC && h2.sequence > h1.sequence)
swap_log_alt(l);
l->pos = l->log_start;
LDUMP("promlog_init", l);
r = 0;
done:
PRINT(("promlog_init: r=%d\n", r));
return r;
}
/*
* promlog_clear
*
* Flashes the entire PROM log to empty and writes the magic,
* version, and sequence number. The sequence number is guaranteed
* to be larger than any previous sequence number (if there was any).
*/
int promlog_clear(promlog_t *l, int init)
{
promlog_header_t h1, h2;
int mask, r;
int sequence;
LDUMP("promlog_clear", l);
if (init) {
if ((r = get_header(l, 0, &h1)) < 0 ||
(r = get_header(l, 1, &h2)) < 0)
goto done;
HDUMP("promlog_clear", &h1);
HDUMP("promlog_clear", &h2);
if (h1.magic == PROMLOG_MAGIC && h2.magic == PROMLOG_MAGIC)
sequence = h1.sequence > h2.sequence ? h1.sequence : h2.sequence;
else if (h1.magic == PROMLOG_MAGIC)
sequence = h1.sequence;
else if (h2.magic == PROMLOG_MAGIC)
sequence = h2.sequence;
else
sequence = 0;
memset(&h1, 0xff, sizeof (h1));
h1.magic = PROMLOG_MAGIC;
h1.version = PROMLOG_VERSION;
h1.sequence = sequence + 1;
mask = ((1 << l->sector_base + 2) -
(1 << l->sector_base ));
PRINT(("promlog: Flashing log sectors, please wait.\n"));
if ((r = fprom_flash_sectors(&l->f, mask)) < 0) {
printf("promlog: Could not flash sectors: %s\n", fprom_errmsg(r));
r = PROMLOG_ERROR_PROM;
goto done;
}
l->active = 0;
set_ranges(l);
l->pos = l->log_start;
if ((r = put_header(l, 0, &h1)) < 0)
goto done;
r = 0;
}
else {
if ((r = promlog_compact(l)) < 0)
goto done;
r = promlog_compact(l);
}
done:
PRINT(("promlog_clear: r=%d\n", r));
return r;
}
/*
* promlog_type
*
* Returns the type of the entry at the current position
* (PROMLOG_TYPE_xxx)
*/
int promlog_type(promlog_t *l)
{
int r;
LDUMP("promlog_type", l);
if ((r = get_entry(l)) >= 0)
r = l->ent.valid ? l->ent.type : PROMLOG_ERROR_POS;
PRINT(("promlog_type: r=%d\n", r));
return r;
}
/*
* promlog_first
*
* Sets the current position to the first valid entry in the log.
*/
int promlog_first(promlog_t *l, int type)
{
int r;
LDUMP("promlog_first", l);
PRINT(("promlog_first: type=%d\n", type));
l->pos = (type == PROMLOG_TYPE_LOG) ? l->alt_start : l->log_start;
if ((r = get_entry(l)) < 0)
goto done;
if (! l->ent.valid || (type != PROMLOG_TYPE_ANY && l->ent.type != type)) {
r = promlog_next(l, type);
goto done;
}
r = 0;
done:
PRINT(("promlog_first: r=%d\n", r));
return r;
}
/*
* promlog_prev
*
* Moves the current position back until it reaches a valid entry of
* the specified type (or PROMLOG_TYPE_ANY). The technique used is
* to search backwards for bit 7 being set (bit 7 can only be set in
* the first byte of an entry). If the pointer is already at the
* start of the log, returns PROMLOG_ERROR_BOL.
*/
int promlog_prev(promlog_t *l, int type)
{
int alt, r;
LDUMP("promlog_prev", l);
PRINT(("promlog_prev: type=%d\n", type));
alt = (l->pos >= l->alt_start && l->pos < l->alt_end);
if (alt && type != PROMLOG_TYPE_LOG && type != PROMLOG_TYPE_ANY) {
l->pos = l->log_start;
alt = 0;
}
while (1) {
if (alt) {
if (l->pos == l->alt_start)
break;
} else if (l->pos == l->log_start) {
if (type != PROMLOG_TYPE_LOG || type != PROMLOG_TYPE_ANY)
break;
l->pos = l->alt_start;
alt = 1;
while (1) {
if ((r = get_entry(l)) < 0)
goto done;
if (l->ent.valid && l->ent.type == PROMLOG_TYPE_END)
break;
l->pos += 4;
}
if (l->pos == l->alt_start)
break;
}
l->pos -= 4;
if ((r = get_entry(l)) < 0)
goto done;
if (l->ent.valid &&
(type == PROMLOG_TYPE_ANY || l->ent.type == type)) {
r = 0;
goto done;
}
}
r = PROMLOG_ERROR_BOL;
done:
LDUMP("promlog_prev", l);
PRINT(("promlog_prev: r=%d\n", r));
return r;
}
/*
* promlog_next
*
* Moves the current position forward until it reaches a valid entry
* of the specified type (or PROMLOG_TYPE_ANY or PROMLOG_TYPE_END).
* If the pointer is already at the end marker, returns PROMLOG_ERROR_EOL.
*/
int promlog_next(promlog_t *l, int type)
{
int alt, r, first;
LDUMP("promlog_next", l);
PRINT(("promlog_next: type=%d\n", type));
alt = (l->pos >= l->alt_start && l->pos < l->alt_end);
if (alt && type != PROMLOG_TYPE_LOG && type != PROMLOG_TYPE_ANY) {
l->pos = l->log_start;
alt = 0;
}
first = 1;
while (1) {
if ((r = get_entry(l)) < 0)
goto done;
if (l->ent.type == PROMLOG_TYPE_END) {
if (first && ! alt) {
r = PROMLOG_ERROR_EOL;
goto done;
}
if (type == PROMLOG_TYPE_ANY || l->ent.type == type)
break; /* Was looking for end and found it */
if (! alt) {
r = PROMLOG_ERROR_EOL;
goto done;
}
l->pos = l->log_start;
alt = 0;
} else if (! first && l->ent.valid &&
(type == PROMLOG_TYPE_ANY || l->ent.type == type))
break; /* Found */
else
l->pos = (l->pos + 2 + l->ent.key_len + l->ent.value_len + 3) & ~3;
first = 0;
}
r = 0;
done:
LDUMP("promlog_next", l);
PRINT(("promlog_next: r=%d\n", r));
return r;
}
/*
* promlog_last
*
* Sets the current position to the end marker. This should be used
* sparingly as it has to search past all the entries in the active
* sector.
*/
int promlog_last(promlog_t *l)
{
int r;
LDUMP("promlog_last", l);
l->pos = l->log_start;
if ((r = get_entry(l)) < 0)
return r;
if (l->ent.type == PROMLOG_TYPE_END)
r = 0;
else
r = promlog_next(l, PROMLOG_TYPE_END);
PRINT(("promlog_last: r=%d\n", r));
return r;
}
/*
* promlog_get
*
* Retrieves the key and value at the current position and stores
* them in the key and value buffers. Current position should be
* pointing to something valid since no error checking is done.
*/
int promlog_get(promlog_t *l, char *key, char *value)
{
int r;
LDUMP("promlog_get", l);
if ((r = get_entry(l)) < 0)
goto done;
if (! l->ent.valid || l->ent.type == PROMLOG_TYPE_END) {
r = PROMLOG_ERROR_POS;
goto done;
}
if (key) {
if ((r = fprom_read(&l->f, l->pos + 2,
key, l->ent.key_len)) < 0) {
printf("promlog: Error reading data at 0x%x: %s\n",
l->pos + 2, fprom_errmsg(r));
r = PROMLOG_ERROR_PROM;
goto done;
}
key[l->ent.key_len] = 0;
}
if (value) {
if ((r = fprom_read(&l->f, l->pos + 2 + l->ent.key_len,
value, l->ent.value_len)) < 0) {
printf("promlog: Error reading data at 0x%x: %s\n",
l->pos + 2 + l->ent.key_len, fprom_errmsg(r));
r = PROMLOG_ERROR_PROM;
goto done;
}
value[l->ent.value_len] = 0;
}
PRINT(("promlog_get: key=%s value=%s\n", key, value));
r = 0;
done:
PRINT(("promlog_get: r=%d\n", r));
return r;
}
/*
* promlog_find
*
* Starts searching at the current position for a key of a given
* type. If found, the current position points to the entry and 0
* is returned. Otherwise, the current position is left at the end
* marker and PROMLOG_ERROR_EOL is returned.
*
* To find the next occurrence of the same key in a list, use
* promlog_next to advance to the following position, then use
* promlog_find again.
*/
int promlog_find(promlog_t *l, char *key, int type)
{
int alt, i, r;
LDUMP("promlog_find", l);
PRINT(("promlog_find: key=%s type=%d\n", key, type));
alt = (l->pos >= l->alt_start && l->pos < l->alt_end);
if (alt && type != PROMLOG_TYPE_LOG && type != PROMLOG_TYPE_ANY) {
l->pos = l->log_start;
alt = 0;
}
if ((i = promlog_type(l)) == PROMLOG_TYPE_END) {
r = PROMLOG_ERROR_EOL;
goto done;
}
if (i == PROMLOG_TYPE_INVALID ||
(type != PROMLOG_TYPE_ANY && type != i))
if ((r = promlog_next(l, type)) < 0)
goto done;
while (1) {
char match[PROMLOG_KEY_MAX];
if ((r = promlog_get(l, match, 0)) < 0)
break;
if (strcmp(match, key) == 0) {
r = 0; /* Found */
break;
}
if ((r = promlog_next(l, type)) < 0)
break; /* Fails at EOF */
}
done:
PRINT(("promlog_find: r=%d\n", r));
return r;
}
/*
* promlog_lookup
*
* Searches the entire log for a variable entry with the specified
* key. If found, stores the value in the value buffer and returns
* 0. If not found, or an error occurs accessing the log, copies
* defl (if non-NULL) to value and returns an error code. Leaves
* the current position at the variable if found, end marker if not
* found.
*/
int promlog_lookup(promlog_t *l, char *key, char *value, char *defl)
{
int r;
LDUMP("promlog_lookup", l);
PRINT(("promlog_lookup: key=%s defl=%s\n", key, defl));
if ((r = promlog_first(l, PROMLOG_TYPE_VAR)) < 0)
goto done;
if ((r = promlog_find(l, key, PROMLOG_TYPE_VAR)) < 0)
goto done;
r = promlog_get(l, 0, value);
done:
if (r < 0 && value) {
if (defl)
strcpy(value, defl);
else
value[0] = 0;
}
PRINT(("promlog_lookup: r=%d\n", r));
return r;
}
/*
* validate_or_replace (internal)
*
* Builds a header structure for an entry that can potentially be
* overwritten, and either checks if the overwrite is possible
* (validate) or does the overwrite (replace).
*
* If validating: returns TRUE if it would be possible to replace
* the entry at the current position with the new data without
* having to make a new log entry. This is true if the replacement
* is of the replacement entry is the same length, and would only
* require changing 1's into 0's and not vice-versa.
*/
static int validate_or_replace(promlog_t *l,
char *key, char *value, int type,
int do_replace)
{
int (*fn)(fprom_t *, fprom_off_t, char *, int);
int r, kl, vl;
LDUMP("validate_or_replace", l);
kl = strlen(key);
vl = strlen(value);
if (kl >= PROMLOG_KEY_MAX || vl >= PROMLOG_VALUE_MAX) {
r = PROMLOG_ERROR_ARG;
goto done;
}
l->ent.valid = 1;
l->ent.type = type;
l->ent.cpu_num = l->cpu_num;
l->ent.key_len = kl;
l->ent.value_len = vl;
EDUMP("validate_or_replace", &l->ent);
fn = do_replace ? fprom_write : fprom_validate;
if ((r = (*fn)(&l->f, l->pos, (char *) &l->ent, 2 )) >= 0 &&
(r = (*fn)(&l->f, l->pos + 2, key, kl)) >= 0 &&
(r = (*fn)(&l->f, l->pos + 2 + kl, value, vl)) >= 0) {
r = do_replace ? 0 : 1;
goto done;
}
if (! do_replace && r == FPROM_ERROR_CONFLICT) {
r = 0;
goto done;
}
printf("promlog: Flash PROM %s error: %s\n",
do_replace ? "write" : "validate", fprom_errmsg(r));
r = PROMLOG_ERROR_PROM;
done:
PRINT(("validate_or_replace: r=%d\n", r));
return r;
}
/*
* promlog_replace
*
* Verifies that a log entry can be written at the specified
* position, and if so, writes it.
*/
int promlog_replace(promlog_t *l, char *key, char *value, int type)
{
int r;
LDUMP("promlog_replace", l);
if ((r = validate_or_replace(l, key, value, type, 0)) < 0)
goto done;
if (r == 0) {
r = PROMLOG_ERROR_REPLACE;
goto done;
}
if ((r = validate_or_replace(l, key, value, type, 1)) < 0)
goto done;
r = 0;
done:
PRINT(("promlog_replace: r=%d\n", r));
return r;
}
/*
* promlog_room
*
* Checks if there would be enough room to make an insertion
* of a specified key and value.
*
* Returns:
* 0 if there is room
* PROMLOG_ERROR_COMPACT if a promlog_compact would create enough room
* PROMLOG_ERROR_FULL if log is full
*/
int promlog_room(promlog_t *l, char *key, char *value)
{
int r;
int need, used;
LDUMP("promlog_room", l);
if ((r = promlog_last(l)) < 0)
goto done;
need = 2 + strlen(key) + strlen(value);
if (l->pos + need <= l->log_end - 4) {
r = 0;
goto done;
}
/*
* Add up the total space currently in use to see if a
* compaction would be futile.
*/
l->pos = l->log_start;
used = 0;
while (1) {
if ((r = get_entry(l)) < 0)
goto done;
if (l->ent.type == PROMLOG_TYPE_END)
break;
if (l->ent.valid &&
(l->ent.type == PROMLOG_TYPE_VAR ||
l->ent.type == PROMLOG_TYPE_LIST)) {
int len = 2 + l->ent.key_len + l->ent.value_len;
used = (used + len + 3) & ~3;
}
if ((r = promlog_next(l, PROMLOG_TYPE_ANY)) < 0)
goto done;
}
if (l->alt_start + used + need > l->alt_end - 4)
r = PROMLOG_ERROR_FULL;
else
r = PROMLOG_ERROR_COMPACT;
done:
PRINT(("promlog_room: r=%d\n", r));
return r;
}
/*
* promlog_compact
*
* Does a sector swap compaction. Leaves the current position at the
* end of the log.
*/
int promlog_compact(promlog_t *l)
{
int mask, r;
char buf[PROMLOG_KEY_MAX + PROMLOG_VALUE_MAX];
int altpos;
promlog_header_t h_old, h_new;
PRINT(("promlog: Compacting log\n"));
/*
* Flash alternate sector
*/
mask = 1 << (l->sector_base + (1 - l->active));
PRINT(("promlog_compact: flash mask %x\n", mask));
if ((r = fprom_flash_sectors(&l->f, mask)) < 0)
goto done;
/*
* Copy variable and list entries from active sector into alternate.
*/
l->pos = l->log_start;
altpos = l->alt_start;
while (1) {
if ((r = get_entry(l)) < 0)
goto done;
if (l->ent.type == PROMLOG_TYPE_END)
break;
if (l->ent.valid &&
(l->ent.type == PROMLOG_TYPE_VAR ||
l->ent.type == PROMLOG_TYPE_LIST)) {
int len = 2 + l->ent.key_len + l->ent.value_len;
if ((r = fprom_read(&l->f, l->pos, buf, len)) < 0) {
printf("promlog: Error reading PROM during compact: %s\n",
fprom_errmsg(r));
r = PROMLOG_ERROR_PROM;
goto done;
}
if ((r = fprom_write(&l->f, altpos, buf, len)) < 0) {
printf("promlog: Error reading PROM during compact: %s\n",
fprom_errmsg(r));
r = PROMLOG_ERROR_PROM;
goto done;
}
altpos = (altpos + len + 3) & ~3;
}
if ((r = promlog_next(l, PROMLOG_TYPE_ANY)) < 0)
goto done;
}
/*
* Write valid header into alternate sector and make it the active one.
*/
if ((r = get_header(l, l->active, &h_old)) < 0)
goto done;
memset(&h_new, 0xff, sizeof (h_new));
h_new.magic = PROMLOG_MAGIC;
h_new.version = PROMLOG_VERSION;
h_new.sequence = h_old.sequence + 1;
if ((r = put_header(l, 1 - l->active, &h_new)) < 0)
goto done;
swap_log_alt(l);
r = promlog_last(l);
done:
if (r < 0)
printf("promlog: Failed (%s)\n", promlog_errmsg(r));
else
PRINT(("promlog: Done\n"));
PRINT(("promlog_compact: r=%d\n", r));
return r;
}
/*
* promlog_put_var
*
* Adds new variable entry. Fails if bit 7 is set in any key or
* value byte (see Log Format). Searches PROM to see if key already
* present If so, Can entry can be patched (only change 1's to 0's)
* If so, does it and returns. Else changes entry to cancelled.
* Room for new entry? If not, swaps log (takes about 2 seconds).
* Leaves current position at end of log.
*/
int promlog_put_var(promlog_t *l, char *key, char *value)
{
int r;
LDUMP("promlog_put_var", l);
r = promlog_first(l, PROMLOG_TYPE_VAR);
if (r < 0 && r != PROMLOG_ERROR_EOL)
goto done;
if (r >= 0) {
r = promlog_find(l, key, PROMLOG_TYPE_VAR);
if (r < 0 && r != PROMLOG_ERROR_EOL)
goto done;
}
if (r >= 0) {
if ((r = validate_or_replace(l, key, value,
PROMLOG_TYPE_VAR, 0)) < 0)
goto done;
if (r) {
r = validate_or_replace(l, key, value,
PROMLOG_TYPE_VAR, 1);
goto done;
}
if ((r = promlog_delete(l)) < 0)
goto done;
}
if ((r = promlog_room(l, key, value)) != 0) {
if (r != PROMLOG_ERROR_COMPACT)
goto done;
if ((r = promlog_compact(l)) < 0)
goto done;
}
r = validate_or_replace(l, key, value, PROMLOG_TYPE_VAR, 1);
done:
PRINT(("promlog_put_var: r=%d\n", r));
return r;
}
/*
* promlog_put_list
*
* Adds new list entry. Fails if bit 7 is set in any key or value
* byte.
*/
int promlog_put_list(promlog_t *l, char *key, char *value)
{
int r;
LDUMP("promlog_put_list", l);
if ((r = promlog_room(l, key, value)) != 0) {
if (r != PROMLOG_ERROR_COMPACT)
goto done;
if ((r = promlog_compact(l)) < 0)
goto done;
}
r = validate_or_replace(l, key, value, PROMLOG_TYPE_LIST, 1);
done:
PRINT(("promlog_put_list: r=%d\n", r));
return r;
}
/*
* promlog_put_log
*
* Adds new log entry. Fails if bit 7 is set in any key or value
* byte. Functions similarly to promlog_put_var.
*/
int promlog_put_log(promlog_t *l, char *key, char *value)
{
int r;
LDUMP("promlog_put_log", l);
if ((r = promlog_room(l, key, value)) != 0) {
if (r != PROMLOG_ERROR_COMPACT)
goto done;
if ((r = promlog_compact(l)) < 0)
goto done;
}
r = validate_or_replace(l, key, value, PROMLOG_TYPE_LOG, 1);
done:
PRINT(("promlog_put_log: r=%d\n", r));
return r;
}
/*
* promlog_delete
*
* Changes the current entry to the cancelled state.
*/
int promlog_delete(promlog_t *l)
{
int r;
LDUMP("promlog_delete", l);
if ((r = get_entry(l)) < 0)
goto done;
if (l->ent.type == PROMLOG_TYPE_END) {
r = PROMLOG_ERROR_POS;
goto done;
}
l->ent.valid = 0;
if ((r = fprom_write(&l->f, l->pos, (char *) &l->ent, 2)) < 0) {
printf("promlog: Error deleting entry: %s\n", fprom_errmsg(r));
r = PROMLOG_ERROR_PROM;
goto done;
}
r = 0;
done:
PRINT(("promlog_delete: r=%d\n", r));
return r;
}
/*
* promlog_dump
*
* Print out information in the promlog structure
*/
static char *type_names[4] = {
"Var", "List", "Log", "End",
};
int promlog_dump(promlog_t *l)
{
int r, type;
char key[PROMLOG_KEY_MAX];
char value[PROMLOG_VALUE_MAX];
#if LDEBUG
int old_mode = debug_mode;
debug_mode = 0;
#endif
printf("PROMLOG DUMP: act=%d ls=0x%x le=0x%x as=0x%x ae=0x%x pos=0x%x\n",
l->active,
l->log_start, l->log_end,
l->alt_start, l->alt_end, l->pos);
printf(" INACTIVE:\n");
swap_log_alt(l);
if ((r = promlog_first(l, PROMLOG_TYPE_ANY)) < 0) {
if (r == PROMLOG_ERROR_EOL)
goto empty1;
goto done;
}
while ((type = promlog_type(l)) != PROMLOG_TYPE_END) {
if ((r = promlog_get(l, key, value)) < 0)
goto done;
printf(" OFFSET(0x%x) TYPE(%d=%s) KEY(%s) VALUE(%s)\n",
l->pos, type, type_names[type], key, value);
if ((r = promlog_next(l, PROMLOG_TYPE_ANY)) < 0) {
if (r == PROMLOG_ERROR_EOL)
break;
goto done;
}
}
printf(" END(0x%x)\n", l->pos);
empty1:
printf(" ACTIVE:\n");
swap_log_alt(l);
if ((r = promlog_first(l, PROMLOG_TYPE_ANY)) < 0) {
if (r == PROMLOG_ERROR_EOL)
goto empty1;
goto done;
}
while ((type = promlog_type(l)) != PROMLOG_TYPE_END) {
if ((r = promlog_get(l, key, value)) < 0)
goto done;
printf(" OFFSET(0x%x) TYPE(%d=%s) KEY(%s) VALUE(%s)\n",
l->pos, type, type_names[type], key, value);
if ((r = promlog_next(l, PROMLOG_TYPE_ANY)) < 0) {
if (r == PROMLOG_ERROR_EOL)
break;
goto done;
}
}
printf(" END(0x%x)\n", l->pos);
#if LDEBUG
debug_mode = old_mode;
#endif
r = (r == PROMLOG_ERROR_EOL) ? 0 : r;
done:
PRINT(("promlog_dump: r=%d\n", r));
return r;
}
/*
* promlog_errmsg
*
* Converts an error code to an error message.
*/
char *promlog_errmsg(int errcode)
{
switch (errcode) {
case PROMLOG_ERROR_NONE:
return "No error";
case PROMLOG_ERROR_PROM:
return "Flash PROM error";
case PROMLOG_ERROR_MAGIC:
return "PROM does not contain valid log";
case PROMLOG_ERROR_CORRUPT:
return "Inconsistent PROM log";
case PROMLOG_ERROR_BOL:
return "Reached beginning of log";
case PROMLOG_ERROR_EOL:
return "Reached end of log";
case PROMLOG_ERROR_POS:
return "Illegal position";
case PROMLOG_ERROR_REPLACE:
return "Illegal replacement attempt";
case PROMLOG_ERROR_COMPACT:
return "Compaction required";
case PROMLOG_ERROR_FULL:
return "PROM log full";
case PROMLOG_ERROR_ARG:
return "Invalid argument";
default:
return "Unknown error code";
}
}