diff options
Diffstat (limited to 'contrib/userspace-nvram/nvram.c')
-rw-r--r-- | contrib/userspace-nvram/nvram.c | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/contrib/userspace-nvram/nvram.c b/contrib/userspace-nvram/nvram.c new file mode 100644 index 0000000000..a2ded149a8 --- /dev/null +++ b/contrib/userspace-nvram/nvram.c @@ -0,0 +1,482 @@ +/* + * NVRAM variable manipulation (common) + * + * Copyright 2004, Broadcom Corporation + * Copyright 2009, OpenWrt.org + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + */ + +#include "nvram.h" + +#define TRACE() \ + printf("%s(%i) in %s()\n", \ + __FILE__, __LINE__, __FUNCTION__) + +/* + * -- Helper functions -- + */ + +/* String hash */ +static uint32_t hash(const char *s) +{ + uint32_t hash = 0; + + while (*s) + hash = 31 * hash + *s++; + + return hash; +} + +/* Free all tuples. */ +static void _nvram_free(nvram_handle_t *h) +{ + uint32_t i; + nvram_tuple_t *t, *next; + + /* Free hash table */ + for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) { + for (t = h->nvram_hash[i]; t; t = next) { + next = t->next; + free(t); + } + h->nvram_hash[i] = NULL; + } + + /* Free dead table */ + for (t = h->nvram_dead; t; t = next) { + next = t->next; + free(t); + } + + h->nvram_dead = NULL; +} + +/* (Re)allocate NVRAM tuples. */ +static nvram_tuple_t * _nvram_realloc( nvram_handle_t *h, nvram_tuple_t *t, + const char *name, const char *value ) +{ + if ((strlen(value) + 1) > NVRAM_SPACE) + return NULL; + + if (!t) { + if (!(t = malloc(sizeof(nvram_tuple_t) + strlen(name) + 1))) + return NULL; + + /* Copy name */ + t->name = (char *) &t[1]; + strcpy(t->name, name); + + t->value = NULL; + } + + /* Copy value */ + if (!t->value || strcmp(t->value, value)) + { + if(!(t->value = (char *) realloc(t->value, strlen(value)+1))) + return NULL; + + strcpy(t->value, value); + t->value[strlen(value)] = '\0'; + } + + return t; +} + +/* (Re)initialize the hash table. */ +static int _nvram_rehash(nvram_handle_t *h) +{ + nvram_header_t *header = (nvram_header_t *) &h->mmap[NVRAM_SPACE]; + char buf[] = "0xXXXXXXXX", *name, *value, *end, *eq; + + /* (Re)initialize hash table */ + _nvram_free(h); + + /* Parse and set "name=value\0 ... \0\0" */ + name = (char *) &header[1]; + /* + end = (char *) header + NVRAM_SPACE - 2; + end[0] = end[1] = '\0'; + */ + for (; *name; name = value + strlen(value) + 1) { + if (!(eq = strchr(name, '='))) + break; + *eq = '\0'; + value = eq + 1; + nvram_set(h, name, value); + *eq = '='; + } + + /* Set special SDRAM parameters */ + if (!nvram_get(h, "sdram_init")) { + sprintf(buf, "0x%04X", (uint16_t)(header->crc_ver_init >> 16)); + nvram_set(h, "sdram_init", buf); + } + if (!nvram_get(h, "sdram_config")) { + sprintf(buf, "0x%04X", (uint16_t)(header->config_refresh & 0xffff)); + nvram_set(h, "sdram_config", buf); + } + if (!nvram_get(h, "sdram_refresh")) { + sprintf(buf, "0x%04X", + (uint16_t)((header->config_refresh >> 16) & 0xffff)); + nvram_set(h, "sdram_refresh", buf); + } + if (!nvram_get(h, "sdram_ncdl")) { + sprintf(buf, "0x%08X", header->config_ncdl); + nvram_set(h, "sdram_ncdl", buf); + } + + return 0; +} + + +/* + * -- Public functions -- + */ + +/* Get the value of an NVRAM variable. */ +char * nvram_get(nvram_handle_t *h, const char *name) +{ + uint32_t i; + nvram_tuple_t *t; + char *value; + + if (!name) + return NULL; + + /* Hash the name */ + i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash); + + /* Find the associated tuple in the hash table */ + for (t = h->nvram_hash[i]; t && strcmp(t->name, name); t = t->next); + + value = t ? t->value : NULL; + + return value; +} + +/* Set the value of an NVRAM variable. */ +int nvram_set(nvram_handle_t *h, const char *name, const char *value) +{ + uint32_t i; + nvram_tuple_t *t, *u, **prev; + + /* Hash the name */ + i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash); + + /* Find the associated tuple in the hash table */ + for (prev = &h->nvram_hash[i], t = *prev; + t && strcmp(t->name, name); prev = &t->next, t = *prev); + + /* (Re)allocate tuple */ + if (!(u = _nvram_realloc(h, t, name, value))) + return -12; /* -ENOMEM */ + + /* Value reallocated */ + if (t && t == u) + return 0; + + /* Move old tuple to the dead table */ + if (t) { + *prev = t->next; + t->next = h->nvram_dead; + h->nvram_dead = t; + } + + /* Add new tuple to the hash table */ + u->next = h->nvram_hash[i]; + h->nvram_hash[i] = u; + + return 0; +} + +/* Unset the value of an NVRAM variable. */ +int nvram_unset(nvram_handle_t *h, const char *name) +{ + uint32_t i; + nvram_tuple_t *t, **prev; + + if (!name) + return 0; + + /* Hash the name */ + i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash); + + /* Find the associated tuple in the hash table */ + for (prev = &h->nvram_hash[i], t = *prev; + t && strcmp(t->name, name); prev = &t->next, t = *prev); + + /* Move it to the dead table */ + if (t) { + *prev = t->next; + t->next = h->nvram_dead; + h->nvram_dead = t; + } + + return 0; +} + +/* Get all NVRAM variables. */ +nvram_tuple_t * nvram_getall(nvram_handle_t *h) +{ + int i; + nvram_tuple_t *t, *l, *x; + + l = NULL; + + for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) { + for (t = h->nvram_hash[i]; t; t = t->next) { + if( (x = (nvram_tuple_t *) malloc(sizeof(nvram_tuple_t))) != NULL ) + { + x->name = t->name; + x->value = t->value; + x->next = l; + l = x; + } + else + { + break; + } + } + } + + return l; +} + +/* Regenerate NVRAM. */ +int nvram_commit(nvram_handle_t *h) +{ + nvram_header_t *header = (nvram_header_t *) &h->mmap[NVRAM_SPACE]; + char *init, *config, *refresh, *ncdl; + char *ptr, *end; + int i; + nvram_tuple_t *t; + nvram_header_t tmp; + uint8_t crc; + + /* Regenerate header */ + header->magic = NVRAM_MAGIC; + header->crc_ver_init = (NVRAM_VERSION << 8); + if (!(init = nvram_get(h, "sdram_init")) || + !(config = nvram_get(h, "sdram_config")) || + !(refresh = nvram_get(h, "sdram_refresh")) || + !(ncdl = nvram_get(h, "sdram_ncdl"))) { + header->crc_ver_init |= SDRAM_INIT << 16; + header->config_refresh = SDRAM_CONFIG; + header->config_refresh |= SDRAM_REFRESH << 16; + header->config_ncdl = 0; + } else { + header->crc_ver_init |= (strtoul(init, NULL, 0) & 0xffff) << 16; + header->config_refresh = strtoul(config, NULL, 0) & 0xffff; + header->config_refresh |= (strtoul(refresh, NULL, 0) & 0xffff) << 16; + header->config_ncdl = strtoul(ncdl, NULL, 0); + } + + /* Clear data area */ + ptr = (char *) header + sizeof(nvram_header_t); + memset(ptr, 0xFF, NVRAM_SPACE - sizeof(nvram_header_t)); + + /* Leave space for a double NUL at the end */ + end = (char *) header + NVRAM_SPACE - 2; + + /* Write out all tuples */ + for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) { + for (t = h->nvram_hash[i]; t; t = t->next) { + if ((ptr + strlen(t->name) + 1 + strlen(t->value) + 1) > end) + break; + ptr += sprintf(ptr, "%s=%s", t->name, t->value) + 1; + } + } + + /* End with a double NUL */ + *ptr = '\0'; + ptr += 2; + + /* Set new length */ + header->len = NVRAM_ROUNDUP(ptr - (char *) header, 4); + + /* Little-endian CRC8 over the last 11 bytes of the header */ + tmp.crc_ver_init = htonl(header->crc_ver_init); + tmp.config_refresh = htonl(header->config_refresh); + tmp.config_ncdl = htonl(header->config_ncdl); + crc = hndcrc8((unsigned char *) &tmp + 9, sizeof(nvram_header_t) - 9, 0xff); + + /* Continue CRC8 over data bytes */ + crc = hndcrc8((unsigned char *) &header[1], + header->len - sizeof(nvram_header_t), crc); + + /* Set new CRC8 */ + header->crc_ver_init |= crc; + + /* Write out */ + msync(h->mmap, h->length, MS_SYNC); + fsync(h->fd); + + /* Reinitialize hash table */ + return _nvram_rehash(h); +} + +/* Open NVRAM and obtain a handle. */ +nvram_handle_t * nvram_open(const char *file, int rdonly) +{ + int fd; + nvram_handle_t *h; + nvram_header_t *header; + + if( (fd = open(file, O_RDWR)) > -1 ) + { + char *mmap_area = (char *) mmap( + NULL, 0x10000, PROT_READ | PROT_WRITE, + ( rdonly == NVRAM_RO ) ? MAP_PRIVATE : MAP_SHARED, fd, 0); + + if( mmap_area != MAP_FAILED ) + { + memset(mmap_area, 0xFF, NVRAM_SPACE); + + if((h = (nvram_handle_t *) malloc(sizeof(nvram_handle_t))) != NULL) + { + memset(h, 0, sizeof(nvram_handle_t)); + + h->fd = fd; + h->mmap = mmap_area; + h->length = 0x10000; + + header = (nvram_header_t *) &h->mmap[NVRAM_SPACE]; + + if( header->magic == NVRAM_MAGIC ) + { + _nvram_rehash(h); + return h; + } + else + { + munmap(h->mmap, h->length); + free(h); + } + } + } + } + + return NULL; +} + +/* Close NVRAM and free memory. */ +int nvram_close(nvram_handle_t *h) +{ + _nvram_free(h); + munmap(h->mmap, h->length); + close(h->fd); + free(h); + + return 0; +} + +/* Determine NVRAM device node. */ +const char * nvram_find_mtd(void) +{ + //return "./samples/nvram.1"; + + FILE *fp; + int i; + char dev[PATH_MAX]; + char *path; + + // "/dev/mtdblock/" + ( 0 < x < 99 ) + "ro" + \0 = 19 + if( (path = (char *) malloc(19)) == NULL ) + return NULL; + + if ((fp = fopen("/proc/mtd", "r"))) { + while (fgets(dev, sizeof(dev), fp)) { + if (sscanf(dev, "mtd%d:", &i) && strstr(dev, "nvram")) { + snprintf(path, 19, "/dev/mtdblock/%d", i); + } + } + fclose(fp); + } + + return (const char *) path; +} + +/* Check NVRAM staging file. */ +const char * nvram_find_staging(void) +{ + struct stat s; + + if( (stat(NVRAM_STAGING, &s) > -1) && (s.st_mode & S_IFREG) ) + { + return NVRAM_STAGING; + } + + return NULL; +} + +/* Copy NVRAM contents to staging file. */ +int nvram_to_staging(void) +{ + int fdmtd, fdstg, stat; + const char *mtd; + char buf[0x10000]; + + stat = -1; + + if( (mtd = nvram_find_mtd()) != NULL ) + { + if( (fdmtd = open(mtd, O_RDONLY)) > -1 ) + { + if( read(fdmtd, buf, sizeof(buf)) == sizeof(buf) ) + { + if((fdstg = open(NVRAM_STAGING, O_WRONLY | O_CREAT, 0600)) > -1) + { + write(fdstg, buf, sizeof(buf)); + fsync(fdstg); + close(fdstg); + + stat = 0; + } + } + + close(fdmtd); + } + } + + return stat; +} + +/* Copy staging file to NVRAM device. */ +int staging_to_nvram(void) +{ + int fdmtd, fdstg, stat; + const char *mtd; + char buf[0x10000]; + + stat = -1; + + if( (mtd = nvram_find_mtd()) != NULL ) + { + if( (fdstg = open(NVRAM_STAGING, O_RDONLY)) > -1 ) + { + if( read(fdstg, buf, sizeof(buf)) == sizeof(buf) ) + { + if( (fdmtd = open(mtd, O_WRONLY | O_SYNC)) > -1 ) + { + write(fdmtd, buf, sizeof(buf)); + fsync(fdmtd); + close(fdmtd); + stat = 0; + } + } + + close(fdstg); + + if( !stat ) + stat = unlink(NVRAM_STAGING) ? 1 : 0; + } + } + + return stat; +} |