2 * NVRAM variable manipulation (common)
4 * Copyright 2004, Broadcom Corporation
5 * Copyright 2009-2010, OpenWrt.org
8 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
9 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
10 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
11 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
18 printf("%s(%i) in %s(): %s\n", \
19 __FILE__, __LINE__, __FUNCTION__, msg ? msg : "?")
21 /* Size of "nvram" MTD partition */
22 size_t nvram_part_size = 0;
26 * -- Helper functions --
30 static uint32_t hash(const char *s)
35 hash = 31 * hash + *s++;
40 /* Free all tuples. */
41 static void _nvram_free(nvram_handle_t *h)
44 nvram_tuple_t *t, *next;
47 for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) {
48 for (t = h->nvram_hash[i]; t; t = next) {
54 h->nvram_hash[i] = NULL;
58 for (t = h->nvram_dead; t; t = next) {
68 /* (Re)allocate NVRAM tuples. */
69 static nvram_tuple_t * _nvram_realloc( nvram_handle_t *h, nvram_tuple_t *t,
70 const char *name, const char *value )
72 if ((strlen(value) + 1) > h->length - h->offset)
76 if (!(t = malloc(sizeof(nvram_tuple_t) + strlen(name) + 1)))
80 t->name = (char *) &t[1];
81 strcpy(t->name, name);
87 if (!t->value || strcmp(t->value, value))
89 if(!(t->value = (char *) realloc(t->value, strlen(value)+1)))
92 strcpy(t->value, value);
93 t->value[strlen(value)] = '\0';
99 /* (Re)initialize the hash table. */
100 static int _nvram_rehash(nvram_handle_t *h)
102 nvram_header_t *header = nvram_header(h);
103 char buf[] = "0xXXXXXXXX", *name, *value, *eq;
105 /* (Re)initialize hash table */
108 /* Parse and set "name=value\0 ... \0\0" */
109 name = (char *) &header[1];
111 for (; *name; name = value + strlen(value) + 1) {
112 if (!(eq = strchr(name, '=')))
116 nvram_set(h, name, value);
120 /* Set special SDRAM parameters */
121 if (!nvram_get(h, "sdram_init")) {
122 sprintf(buf, "0x%04X", (uint16_t)(header->crc_ver_init >> 16));
123 nvram_set(h, "sdram_init", buf);
125 if (!nvram_get(h, "sdram_config")) {
126 sprintf(buf, "0x%04X", (uint16_t)(header->config_refresh & 0xffff));
127 nvram_set(h, "sdram_config", buf);
129 if (!nvram_get(h, "sdram_refresh")) {
130 sprintf(buf, "0x%04X",
131 (uint16_t)((header->config_refresh >> 16) & 0xffff));
132 nvram_set(h, "sdram_refresh", buf);
134 if (!nvram_get(h, "sdram_ncdl")) {
135 sprintf(buf, "0x%08X", header->config_ncdl);
136 nvram_set(h, "sdram_ncdl", buf);
144 * -- Public functions --
147 /* Get nvram header. */
148 nvram_header_t * nvram_header(nvram_handle_t *h)
150 return (nvram_header_t *) &h->mmap[h->offset];
153 /* Get the value of an NVRAM variable. */
154 char * nvram_get(nvram_handle_t *h, const char *name)
164 i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash);
166 /* Find the associated tuple in the hash table */
167 for (t = h->nvram_hash[i]; t && strcmp(t->name, name); t = t->next);
169 value = t ? t->value : NULL;
174 /* Set the value of an NVRAM variable. */
175 int nvram_set(nvram_handle_t *h, const char *name, const char *value)
178 nvram_tuple_t *t, *u, **prev;
181 i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash);
183 /* Find the associated tuple in the hash table */
184 for (prev = &h->nvram_hash[i], t = *prev;
185 t && strcmp(t->name, name); prev = &t->next, t = *prev);
187 /* (Re)allocate tuple */
188 if (!(u = _nvram_realloc(h, t, name, value)))
189 return -12; /* -ENOMEM */
191 /* Value reallocated */
195 /* Move old tuple to the dead table */
198 t->next = h->nvram_dead;
202 /* Add new tuple to the hash table */
203 u->next = h->nvram_hash[i];
204 h->nvram_hash[i] = u;
209 /* Unset the value of an NVRAM variable. */
210 int nvram_unset(nvram_handle_t *h, const char *name)
213 nvram_tuple_t *t, **prev;
219 i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash);
221 /* Find the associated tuple in the hash table */
222 for (prev = &h->nvram_hash[i], t = *prev;
223 t && strcmp(t->name, name); prev = &t->next, t = *prev);
225 /* Move it to the dead table */
228 t->next = h->nvram_dead;
235 /* Get all NVRAM variables. */
236 nvram_tuple_t * nvram_getall(nvram_handle_t *h)
239 nvram_tuple_t *t, *l, *x;
243 for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) {
244 for (t = h->nvram_hash[i]; t; t = t->next) {
245 if( (x = (nvram_tuple_t *) malloc(sizeof(nvram_tuple_t))) != NULL )
262 /* Regenerate NVRAM. */
263 int nvram_commit(nvram_handle_t *h)
265 nvram_header_t *header = nvram_header(h);
266 char *init, *config, *refresh, *ncdl;
273 /* Regenerate header */
274 header->magic = NVRAM_MAGIC;
275 header->crc_ver_init = (NVRAM_VERSION << 8);
276 if (!(init = nvram_get(h, "sdram_init")) ||
277 !(config = nvram_get(h, "sdram_config")) ||
278 !(refresh = nvram_get(h, "sdram_refresh")) ||
279 !(ncdl = nvram_get(h, "sdram_ncdl"))) {
280 header->crc_ver_init |= SDRAM_INIT << 16;
281 header->config_refresh = SDRAM_CONFIG;
282 header->config_refresh |= SDRAM_REFRESH << 16;
283 header->config_ncdl = 0;
285 header->crc_ver_init |= (strtoul(init, NULL, 0) & 0xffff) << 16;
286 header->config_refresh = strtoul(config, NULL, 0) & 0xffff;
287 header->config_refresh |= (strtoul(refresh, NULL, 0) & 0xffff) << 16;
288 header->config_ncdl = strtoul(ncdl, NULL, 0);
291 /* Clear data area */
292 ptr = (char *) header + sizeof(nvram_header_t);
293 memset(ptr, 0xFF, nvram_part_size - h->offset - sizeof(nvram_header_t));
294 memset(&tmp, 0, sizeof(nvram_header_t));
296 /* Leave space for a double NUL at the end */
297 end = (char *) header + nvram_part_size - h->offset - 2;
299 /* Write out all tuples */
300 for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) {
301 for (t = h->nvram_hash[i]; t; t = t->next) {
302 if ((ptr + strlen(t->name) + 1 + strlen(t->value) + 1) > end)
304 ptr += sprintf(ptr, "%s=%s", t->name, t->value) + 1;
308 /* End with a double NULL and pad to 4 bytes */
313 memset(ptr, 0, 4 - ((int)ptr % 4));
318 header->len = NVRAM_ROUNDUP(ptr - (char *) header, 4);
320 /* Little-endian CRC8 over the last 11 bytes of the header */
321 tmp.crc_ver_init = header->crc_ver_init;
322 tmp.config_refresh = header->config_refresh;
323 tmp.config_ncdl = header->config_ncdl;
324 crc = hndcrc8((unsigned char *) &tmp + NVRAM_CRC_START_POSITION,
325 sizeof(nvram_header_t) - NVRAM_CRC_START_POSITION, 0xff);
327 /* Continue CRC8 over data bytes */
328 crc = hndcrc8((unsigned char *) &header[0] + sizeof(nvram_header_t),
329 header->len - sizeof(nvram_header_t), crc);
332 header->crc_ver_init |= crc;
335 msync(h->mmap, h->length, MS_SYNC);
338 /* Reinitialize hash table */
339 return _nvram_rehash(h);
342 /* Open NVRAM and obtain a handle. */
343 nvram_handle_t * nvram_open(const char *file, int rdonly)
349 nvram_header_t *header;
352 /* If erase size or file are undefined then try to define them */
353 if( (nvram_part_size == 0) || (file == NULL) )
355 /* Finding the mtd will set the appropriate erase size */
356 if( (mtd = nvram_find_mtd()) == NULL || nvram_part_size == 0 )
363 if( (fd = open(file ? file : mtd, O_RDWR)) > -1 )
365 char *mmap_area = (char *) mmap(
366 NULL, nvram_part_size, PROT_READ | PROT_WRITE,
367 (( rdonly == NVRAM_RO ) ? MAP_PRIVATE : MAP_SHARED) | MAP_LOCKED, fd, 0);
369 if( mmap_area != MAP_FAILED )
372 * Start looking for NVRAM_MAGIC at beginning of MTD
373 * partition. Stop if there is less than NVRAM_MIN_SPACE
374 * to check, that was the lowest used size.
376 for( i = 0; i <= ((nvram_part_size - NVRAM_MIN_SPACE) / sizeof(uint32_t)); i++ )
378 if( ((uint32_t *)mmap_area)[i] == NVRAM_MAGIC )
380 offset = i * sizeof(uint32_t);
387 munmap(mmap_area, nvram_part_size);
392 else if( (h = malloc(sizeof(nvram_handle_t))) != NULL )
394 memset(h, 0, sizeof(nvram_handle_t));
398 h->length = nvram_part_size;
401 header = nvram_header(h);
403 if (header->magic == NVRAM_MAGIC &&
404 (rdonly || header->len < h->length - h->offset)) {
411 munmap(h->mmap, h->length);
423 /* Close NVRAM and free memory. */
424 int nvram_close(nvram_handle_t *h)
427 munmap(h->mmap, h->length);
434 /* Determine NVRAM device node. */
435 char * nvram_find_mtd(void)
443 if ((fp = fopen("/proc/mtd", "r")))
445 while( fgets(dev, sizeof(dev), fp) )
447 if( strstr(dev, "nvram") && sscanf(dev, "mtd%d: %08x", &i, &part_size) )
449 nvram_part_size = part_size;
451 sprintf(dev, "/dev/mtdblock%d", i);
452 if( stat(dev, &s) > -1 && (s.st_mode & S_IFBLK) )
454 if( (path = (char *) malloc(strlen(dev)+1)) != NULL )
456 strncpy(path, dev, strlen(dev)+1);
468 /* Check NVRAM staging file. */
469 char * nvram_find_staging(void)
473 if( (stat(NVRAM_STAGING, &s) > -1) && (s.st_mode & S_IFREG) )
475 return NVRAM_STAGING;
481 /* Copy NVRAM contents to staging file. */
482 int nvram_to_staging(void)
484 int fdmtd, fdstg, stat;
485 char *mtd = nvram_find_mtd();
486 char buf[nvram_part_size];
490 if( (mtd != NULL) && (nvram_part_size > 0) )
492 if( (fdmtd = open(mtd, O_RDONLY)) > -1 )
494 if( read(fdmtd, buf, sizeof(buf)) == sizeof(buf) )
496 if((fdstg = open(NVRAM_STAGING, O_WRONLY | O_CREAT, 0600)) > -1)
498 write(fdstg, buf, sizeof(buf));
514 /* Copy staging file to NVRAM device. */
515 int staging_to_nvram(void)
517 int fdmtd, fdstg, stat;
518 char *mtd = nvram_find_mtd();
519 char buf[nvram_part_size];
523 if( (mtd != NULL) && (nvram_part_size > 0) )
525 if( (fdstg = open(NVRAM_STAGING, O_RDONLY)) > -1 )
527 if( read(fdstg, buf, sizeof(buf)) == sizeof(buf) )
529 if( (fdmtd = open(mtd, O_WRONLY | O_SYNC)) > -1 )
531 write(fdmtd, buf, sizeof(buf));
541 stat = unlink(NVRAM_STAGING) ? 1 : 0;