2 * NVRAM variable manipulation (common)
4 * Copyright 2004, Broadcom Corporation
5 * Copyright 2009, 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_t nvram_erase_size
= 0;
25 * -- Helper functions --
29 static uint32_t hash(const char *s
)
34 hash
= 31 * hash
+ *s
++;
39 /* Free all tuples. */
40 static void _nvram_free(nvram_handle_t
*h
)
43 nvram_tuple_t
*t
, *next
;
46 for (i
= 0; i
< NVRAM_ARRAYSIZE(h
->nvram_hash
); i
++) {
47 for (t
= h
->nvram_hash
[i
]; t
; t
= next
) {
51 h
->nvram_hash
[i
] = NULL
;
55 for (t
= h
->nvram_dead
; t
; t
= next
) {
63 /* (Re)allocate NVRAM tuples. */
64 static nvram_tuple_t
* _nvram_realloc( nvram_handle_t
*h
, nvram_tuple_t
*t
,
65 const char *name
, const char *value
)
67 if ((strlen(value
) + 1) > NVRAM_SPACE
)
71 if (!(t
= malloc(sizeof(nvram_tuple_t
) + strlen(name
) + 1)))
75 t
->name
= (char *) &t
[1];
76 strcpy(t
->name
, name
);
82 if (!t
->value
|| strcmp(t
->value
, value
))
84 if(!(t
->value
= (char *) realloc(t
->value
, strlen(value
)+1)))
87 strcpy(t
->value
, value
);
88 t
->value
[strlen(value
)] = '\0';
94 /* (Re)initialize the hash table. */
95 static int _nvram_rehash(nvram_handle_t
*h
)
97 nvram_header_t
*header
= (nvram_header_t
*) &h
->mmap
[NVRAM_START(nvram_erase_size
)];
98 char buf
[] = "0xXXXXXXXX", *name
, *value
, *eq
;
100 /* (Re)initialize hash table */
103 /* Parse and set "name=value\0 ... \0\0" */
104 name
= (char *) &header
[1];
106 for (; *name
; name
= value
+ strlen(value
) + 1) {
107 if (!(eq
= strchr(name
, '=')))
111 nvram_set(h
, name
, value
);
115 /* Set special SDRAM parameters */
116 if (!nvram_get(h
, "sdram_init")) {
117 sprintf(buf
, "0x%04X", (uint16_t)(header
->crc_ver_init
>> 16));
118 nvram_set(h
, "sdram_init", buf
);
120 if (!nvram_get(h
, "sdram_config")) {
121 sprintf(buf
, "0x%04X", (uint16_t)(header
->config_refresh
& 0xffff));
122 nvram_set(h
, "sdram_config", buf
);
124 if (!nvram_get(h
, "sdram_refresh")) {
125 sprintf(buf
, "0x%04X",
126 (uint16_t)((header
->config_refresh
>> 16) & 0xffff));
127 nvram_set(h
, "sdram_refresh", buf
);
129 if (!nvram_get(h
, "sdram_ncdl")) {
130 sprintf(buf
, "0x%08X", header
->config_ncdl
);
131 nvram_set(h
, "sdram_ncdl", buf
);
139 * -- Public functions --
142 /* Get the value of an NVRAM variable. */
143 char * nvram_get(nvram_handle_t
*h
, const char *name
)
153 i
= hash(name
) % NVRAM_ARRAYSIZE(h
->nvram_hash
);
155 /* Find the associated tuple in the hash table */
156 for (t
= h
->nvram_hash
[i
]; t
&& strcmp(t
->name
, name
); t
= t
->next
);
158 value
= t
? t
->value
: NULL
;
163 /* Set the value of an NVRAM variable. */
164 int nvram_set(nvram_handle_t
*h
, const char *name
, const char *value
)
167 nvram_tuple_t
*t
, *u
, **prev
;
170 i
= hash(name
) % NVRAM_ARRAYSIZE(h
->nvram_hash
);
172 /* Find the associated tuple in the hash table */
173 for (prev
= &h
->nvram_hash
[i
], t
= *prev
;
174 t
&& strcmp(t
->name
, name
); prev
= &t
->next
, t
= *prev
);
176 /* (Re)allocate tuple */
177 if (!(u
= _nvram_realloc(h
, t
, name
, value
)))
178 return -12; /* -ENOMEM */
180 /* Value reallocated */
184 /* Move old tuple to the dead table */
187 t
->next
= h
->nvram_dead
;
191 /* Add new tuple to the hash table */
192 u
->next
= h
->nvram_hash
[i
];
193 h
->nvram_hash
[i
] = u
;
198 /* Unset the value of an NVRAM variable. */
199 int nvram_unset(nvram_handle_t
*h
, const char *name
)
202 nvram_tuple_t
*t
, **prev
;
208 i
= hash(name
) % NVRAM_ARRAYSIZE(h
->nvram_hash
);
210 /* Find the associated tuple in the hash table */
211 for (prev
= &h
->nvram_hash
[i
], t
= *prev
;
212 t
&& strcmp(t
->name
, name
); prev
= &t
->next
, t
= *prev
);
214 /* Move it to the dead table */
217 t
->next
= h
->nvram_dead
;
224 /* Get all NVRAM variables. */
225 nvram_tuple_t
* nvram_getall(nvram_handle_t
*h
)
228 nvram_tuple_t
*t
, *l
, *x
;
232 for (i
= 0; i
< NVRAM_ARRAYSIZE(h
->nvram_hash
); i
++) {
233 for (t
= h
->nvram_hash
[i
]; t
; t
= t
->next
) {
234 if( (x
= (nvram_tuple_t
*) malloc(sizeof(nvram_tuple_t
))) != NULL
)
251 /* Regenerate NVRAM. */
252 int nvram_commit(nvram_handle_t
*h
)
254 nvram_header_t
*header
= (nvram_header_t
*) &h
->mmap
[NVRAM_START(nvram_erase_size
)];
255 char *init
, *config
, *refresh
, *ncdl
;
262 /* Regenerate header */
263 header
->magic
= NVRAM_MAGIC
;
264 header
->crc_ver_init
= (NVRAM_VERSION
<< 8);
265 if (!(init
= nvram_get(h
, "sdram_init")) ||
266 !(config
= nvram_get(h
, "sdram_config")) ||
267 !(refresh
= nvram_get(h
, "sdram_refresh")) ||
268 !(ncdl
= nvram_get(h
, "sdram_ncdl"))) {
269 header
->crc_ver_init
|= SDRAM_INIT
<< 16;
270 header
->config_refresh
= SDRAM_CONFIG
;
271 header
->config_refresh
|= SDRAM_REFRESH
<< 16;
272 header
->config_ncdl
= 0;
274 header
->crc_ver_init
|= (strtoul(init
, NULL
, 0) & 0xffff) << 16;
275 header
->config_refresh
= strtoul(config
, NULL
, 0) & 0xffff;
276 header
->config_refresh
|= (strtoul(refresh
, NULL
, 0) & 0xffff) << 16;
277 header
->config_ncdl
= strtoul(ncdl
, NULL
, 0);
280 /* Clear data area */
281 ptr
= (char *) header
+ sizeof(nvram_header_t
);
282 memset(ptr
, 0xFF, NVRAM_SPACE
- sizeof(nvram_header_t
));
283 memset(&tmp
, 0, sizeof(nvram_header_t
));
285 /* Leave space for a double NUL at the end */
286 end
= (char *) header
+ NVRAM_SPACE
- 2;
288 /* Write out all tuples */
289 for (i
= 0; i
< NVRAM_ARRAYSIZE(h
->nvram_hash
); i
++) {
290 for (t
= h
->nvram_hash
[i
]; t
; t
= t
->next
) {
291 if ((ptr
+ strlen(t
->name
) + 1 + strlen(t
->value
) + 1) > end
)
293 ptr
+= sprintf(ptr
, "%s=%s", t
->name
, t
->value
) + 1;
297 /* End with a double NUL */
302 header
->len
= NVRAM_ROUNDUP(ptr
- (char *) header
, 4);
304 /* Little-endian CRC8 over the last 11 bytes of the header */
305 tmp
.crc_ver_init
= htonl(header
->crc_ver_init
);
306 tmp
.config_refresh
= htonl(header
->config_refresh
);
307 tmp
.config_ncdl
= htonl(header
->config_ncdl
);
308 crc
= hndcrc8((unsigned char *) &tmp
+ 9, sizeof(nvram_header_t
) - 9, 0xff);
310 /* Continue CRC8 over data bytes */
311 crc
= hndcrc8((unsigned char *) &header
[1],
312 header
->len
- sizeof(nvram_header_t
), crc
);
315 header
->crc_ver_init
|= crc
;
318 msync(h
->mmap
, h
->length
, MS_SYNC
);
321 /* Reinitialize hash table */
322 return _nvram_rehash(h
);
325 /* Open NVRAM and obtain a handle. */
326 nvram_handle_t
* nvram_open(const char *file
, int rdonly
)
330 nvram_header_t
*header
;
332 /* If erase size or file are undefined then try to define them */
333 if( (nvram_erase_size
== 0) || (file
== NULL
) )
335 /* Finding the mtd will set the appropriate erase size */
337 file
= nvram_find_mtd();
339 (void) nvram_find_mtd();
341 if( nvram_erase_size
== 0 )
345 if( (fd
= open(file
, O_RDWR
)) > -1 )
347 char *mmap_area
= (char *) mmap(
348 NULL
, nvram_erase_size
, PROT_READ
| PROT_WRITE
,
349 ( rdonly
== NVRAM_RO
) ? MAP_PRIVATE
: MAP_SHARED
, fd
, 0);
351 if( mmap_area
!= MAP_FAILED
)
353 memset(mmap_area
, 0xFF, NVRAM_START(nvram_erase_size
));
355 if((h
= (nvram_handle_t
*) malloc(sizeof(nvram_handle_t
))) != NULL
)
357 memset(h
, 0, sizeof(nvram_handle_t
));
361 h
->length
= nvram_erase_size
;
363 header
= (nvram_header_t
*) &h
->mmap
[NVRAM_START(nvram_erase_size
)];
365 if( header
->magic
== NVRAM_MAGIC
)
372 munmap(h
->mmap
, h
->length
);
382 /* Close NVRAM and free memory. */
383 int nvram_close(nvram_handle_t
*h
)
386 munmap(h
->mmap
, h
->length
);
393 /* Determine NVRAM device node. */
394 const char * nvram_find_mtd(void)
401 // "/dev/mtdblock/" + ( 0 < x < 99 ) + \0 = 19
402 if( (path
= (char *) malloc(19)) == NULL
)
405 if ((fp
= fopen("/proc/mtd", "r"))) {
406 while (fgets(dev
, sizeof(dev
), fp
)) {
407 if (strstr(dev
, "nvram") && sscanf(dev
, "mtd%d: %08x", &i
, &esz
)) {
408 if( (path
= (char *) malloc(19)) != NULL
)
410 nvram_erase_size
= esz
;
411 snprintf(path
, 19, "/dev/mtdblock/%d", i
);
422 /* Check NVRAM staging file. */
423 const char * nvram_find_staging(void)
427 if( (stat(NVRAM_STAGING
, &s
) > -1) && (s
.st_mode
& S_IFREG
) )
429 return NVRAM_STAGING
;
435 /* Copy NVRAM contents to staging file. */
436 int nvram_to_staging(void)
438 int fdmtd
, fdstg
, stat
;
439 const char *mtd
= nvram_find_mtd();
440 char buf
[nvram_erase_size
];
444 if( (mtd
!= NULL
) && (nvram_erase_size
> 0) )
446 if( (fdmtd
= open(mtd
, O_RDONLY
)) > -1 )
448 if( read(fdmtd
, buf
, sizeof(buf
)) == sizeof(buf
) )
450 if((fdstg
= open(NVRAM_STAGING
, O_WRONLY
| O_CREAT
, 0600)) > -1)
452 write(fdstg
, buf
, sizeof(buf
));
467 /* Copy staging file to NVRAM device. */
468 int staging_to_nvram(void)
470 int fdmtd
, fdstg
, stat
;
471 const char *mtd
= nvram_find_mtd();
472 char buf
[nvram_erase_size
];
476 if( (mtd
!= NULL
) && (nvram_erase_size
> 0) )
478 if( (fdstg
= open(NVRAM_STAGING
, O_RDONLY
)) > -1 )
480 if( read(fdstg
, buf
, sizeof(buf
)) == sizeof(buf
) )
482 if( (fdmtd
= open(mtd
, O_WRONLY
| O_SYNC
)) > -1 )
484 write(fdmtd
, buf
, sizeof(buf
));
494 stat
= unlink(NVRAM_STAGING
) ? 1 : 0;