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;