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
) {
52 h
->nvram_hash
[i
] = NULL
;
56 for (t
= h
->nvram_dead
; t
; t
= next
) {
64 /* (Re)allocate NVRAM tuples. */
65 static nvram_tuple_t
* _nvram_realloc( nvram_handle_t
*h
, nvram_tuple_t
*t
,
66 const char *name
, const char *value
)
68 if ((strlen(value
) + 1) > NVRAM_SPACE
)
72 if (!(t
= malloc(sizeof(nvram_tuple_t
) + strlen(name
) + 1)))
76 t
->name
= (char *) &t
[1];
77 strcpy(t
->name
, name
);
83 if (!t
->value
|| strcmp(t
->value
, value
))
85 if(!(t
->value
= (char *) realloc(t
->value
, strlen(value
)+1)))
88 strcpy(t
->value
, value
);
89 t
->value
[strlen(value
)] = '\0';
95 /* (Re)initialize the hash table. */
96 static int _nvram_rehash(nvram_handle_t
*h
)
98 nvram_header_t
*header
= nvram_header(h
);
99 char buf
[] = "0xXXXXXXXX", *name
, *value
, *eq
;
101 /* (Re)initialize hash table */
104 /* Parse and set "name=value\0 ... \0\0" */
105 name
= (char *) &header
[1];
107 for (; *name
; name
= value
+ strlen(value
) + 1) {
108 if (!(eq
= strchr(name
, '=')))
112 nvram_set(h
, name
, value
);
116 /* Set special SDRAM parameters */
117 if (!nvram_get(h
, "sdram_init")) {
118 sprintf(buf
, "0x%04X", (uint16_t)(header
->crc_ver_init
>> 16));
119 nvram_set(h
, "sdram_init", buf
);
121 if (!nvram_get(h
, "sdram_config")) {
122 sprintf(buf
, "0x%04X", (uint16_t)(header
->config_refresh
& 0xffff));
123 nvram_set(h
, "sdram_config", buf
);
125 if (!nvram_get(h
, "sdram_refresh")) {
126 sprintf(buf
, "0x%04X",
127 (uint16_t)((header
->config_refresh
>> 16) & 0xffff));
128 nvram_set(h
, "sdram_refresh", buf
);
130 if (!nvram_get(h
, "sdram_ncdl")) {
131 sprintf(buf
, "0x%08X", header
->config_ncdl
);
132 nvram_set(h
, "sdram_ncdl", buf
);
140 * -- Public functions --
143 /* Get nvram header. */
144 nvram_header_t
* nvram_header(nvram_handle_t
*h
)
146 return (nvram_header_t
*) &h
->mmap
[h
->offset
];
149 /* Get the value of an NVRAM variable. */
150 char * nvram_get(nvram_handle_t
*h
, const char *name
)
160 i
= hash(name
) % NVRAM_ARRAYSIZE(h
->nvram_hash
);
162 /* Find the associated tuple in the hash table */
163 for (t
= h
->nvram_hash
[i
]; t
&& strcmp(t
->name
, name
); t
= t
->next
);
165 value
= t
? t
->value
: NULL
;
170 /* Set the value of an NVRAM variable. */
171 int nvram_set(nvram_handle_t
*h
, const char *name
, const char *value
)
174 nvram_tuple_t
*t
, *u
, **prev
;
177 i
= hash(name
) % NVRAM_ARRAYSIZE(h
->nvram_hash
);
179 /* Find the associated tuple in the hash table */
180 for (prev
= &h
->nvram_hash
[i
], t
= *prev
;
181 t
&& strcmp(t
->name
, name
); prev
= &t
->next
, t
= *prev
);
183 /* (Re)allocate tuple */
184 if (!(u
= _nvram_realloc(h
, t
, name
, value
)))
185 return -12; /* -ENOMEM */
187 /* Value reallocated */
191 /* Move old tuple to the dead table */
194 t
->next
= h
->nvram_dead
;
198 /* Add new tuple to the hash table */
199 u
->next
= h
->nvram_hash
[i
];
200 h
->nvram_hash
[i
] = u
;
205 /* Unset the value of an NVRAM variable. */
206 int nvram_unset(nvram_handle_t
*h
, const char *name
)
209 nvram_tuple_t
*t
, **prev
;
215 i
= hash(name
) % NVRAM_ARRAYSIZE(h
->nvram_hash
);
217 /* Find the associated tuple in the hash table */
218 for (prev
= &h
->nvram_hash
[i
], t
= *prev
;
219 t
&& strcmp(t
->name
, name
); prev
= &t
->next
, t
= *prev
);
221 /* Move it to the dead table */
224 t
->next
= h
->nvram_dead
;
231 /* Get all NVRAM variables. */
232 nvram_tuple_t
* nvram_getall(nvram_handle_t
*h
)
235 nvram_tuple_t
*t
, *l
, *x
;
239 for (i
= 0; i
< NVRAM_ARRAYSIZE(h
->nvram_hash
); i
++) {
240 for (t
= h
->nvram_hash
[i
]; t
; t
= t
->next
) {
241 if( (x
= (nvram_tuple_t
*) malloc(sizeof(nvram_tuple_t
))) != NULL
)
258 /* Regenerate NVRAM. */
259 int nvram_commit(nvram_handle_t
*h
)
261 nvram_header_t
*header
= nvram_header(h
);
262 char *init
, *config
, *refresh
, *ncdl
;
269 /* Regenerate header */
270 header
->magic
= NVRAM_MAGIC
;
271 header
->crc_ver_init
= (NVRAM_VERSION
<< 8);
272 if (!(init
= nvram_get(h
, "sdram_init")) ||
273 !(config
= nvram_get(h
, "sdram_config")) ||
274 !(refresh
= nvram_get(h
, "sdram_refresh")) ||
275 !(ncdl
= nvram_get(h
, "sdram_ncdl"))) {
276 header
->crc_ver_init
|= SDRAM_INIT
<< 16;
277 header
->config_refresh
= SDRAM_CONFIG
;
278 header
->config_refresh
|= SDRAM_REFRESH
<< 16;
279 header
->config_ncdl
= 0;
281 header
->crc_ver_init
|= (strtoul(init
, NULL
, 0) & 0xffff) << 16;
282 header
->config_refresh
= strtoul(config
, NULL
, 0) & 0xffff;
283 header
->config_refresh
|= (strtoul(refresh
, NULL
, 0) & 0xffff) << 16;
284 header
->config_ncdl
= strtoul(ncdl
, NULL
, 0);
287 /* Clear data area */
288 ptr
= (char *) header
+ sizeof(nvram_header_t
);
289 memset(ptr
, 0xFF, nvram_part_size
- h
->offset
- sizeof(nvram_header_t
));
290 memset(&tmp
, 0, sizeof(nvram_header_t
));
292 /* Leave space for a double NUL at the end */
293 end
= (char *) header
+ nvram_part_size
- h
->offset
- 2;
295 /* Write out all tuples */
296 for (i
= 0; i
< NVRAM_ARRAYSIZE(h
->nvram_hash
); i
++) {
297 for (t
= h
->nvram_hash
[i
]; t
; t
= t
->next
) {
298 if ((ptr
+ strlen(t
->name
) + 1 + strlen(t
->value
) + 1) > end
)
300 ptr
+= sprintf(ptr
, "%s=%s", t
->name
, t
->value
) + 1;
304 /* End with a double NULL and pad to 4 bytes */
309 memset(ptr
, 0, 4 - ((int)ptr
% 4));
314 header
->len
= NVRAM_ROUNDUP(ptr
- (char *) header
, 4);
316 /* Little-endian CRC8 over the last 11 bytes of the header */
317 tmp
.crc_ver_init
= header
->crc_ver_init
;
318 tmp
.config_refresh
= header
->config_refresh
;
319 tmp
.config_ncdl
= header
->config_ncdl
;
320 crc
= hndcrc8((unsigned char *) &tmp
+ NVRAM_CRC_START_POSITION
,
321 sizeof(nvram_header_t
) - NVRAM_CRC_START_POSITION
, 0xff);
323 /* Continue CRC8 over data bytes */
324 crc
= hndcrc8((unsigned char *) &header
[0] + sizeof(nvram_header_t
),
325 header
->len
- sizeof(nvram_header_t
), crc
);
328 header
->crc_ver_init
|= crc
;
331 msync(h
->mmap
, h
->length
, MS_SYNC
);
334 /* Reinitialize hash table */
335 return _nvram_rehash(h
);
338 /* Open NVRAM and obtain a handle. */
339 nvram_handle_t
* nvram_open(const char *file
, int rdonly
)
345 nvram_header_t
*header
;
348 /* If erase size or file are undefined then try to define them */
349 if( (nvram_part_size
== 0) || (file
== NULL
) )
351 /* Finding the mtd will set the appropriate erase size */
352 if( (mtd
= nvram_find_mtd()) == NULL
|| nvram_part_size
== 0 )
359 if( (fd
= open(file
? file
: mtd
, O_RDWR
)) > -1 )
361 char *mmap_area
= (char *) mmap(
362 NULL
, nvram_part_size
, PROT_READ
| PROT_WRITE
,
363 (( rdonly
== NVRAM_RO
) ? MAP_PRIVATE
: MAP_SHARED
) | MAP_LOCKED
, fd
, 0);
365 if( mmap_area
!= MAP_FAILED
)
368 * Start looking for NVRAM_MAGIC at beginning of MTD
369 * partition. Stop if there is less than NVRAM_MIN_SPACE
370 * to check, that was the lowest used size.
372 for( i
= 0; i
<= ((nvram_part_size
- NVRAM_MIN_SPACE
) / sizeof(uint32_t)); i
++ )
374 if( ((uint32_t *)mmap_area
)[i
] == NVRAM_MAGIC
)
376 offset
= i
* sizeof(uint32_t);
386 else if( (h
= malloc(sizeof(nvram_handle_t
))) != NULL
)
388 memset(h
, 0, sizeof(nvram_handle_t
));
392 h
->length
= nvram_part_size
;
395 header
= nvram_header(h
);
397 if (header
->magic
== NVRAM_MAGIC
&&
398 (rdonly
|| header
->len
< NVRAM_SPACE
)) {
405 munmap(h
->mmap
, h
->length
);
416 /* Close NVRAM and free memory. */
417 int nvram_close(nvram_handle_t
*h
)
420 munmap(h
->mmap
, h
->length
);
427 /* Determine NVRAM device node. */
428 char * nvram_find_mtd(void)
436 if ((fp
= fopen("/proc/mtd", "r")))
438 while( fgets(dev
, sizeof(dev
), fp
) )
440 if( strstr(dev
, "nvram") && sscanf(dev
, "mtd%d: %08x", &i
, &part_size
) )
442 nvram_part_size
= part_size
;
444 sprintf(dev
, "/dev/mtdblock%d", i
);
445 if( stat(dev
, &s
) > -1 && (s
.st_mode
& S_IFBLK
) )
447 if( (path
= (char *) malloc(strlen(dev
)+1)) != NULL
)
449 strncpy(path
, dev
, strlen(dev
)+1);
461 /* Check NVRAM staging file. */
462 char * nvram_find_staging(void)
466 if( (stat(NVRAM_STAGING
, &s
) > -1) && (s
.st_mode
& S_IFREG
) )
468 return NVRAM_STAGING
;
474 /* Copy NVRAM contents to staging file. */
475 int nvram_to_staging(void)
477 int fdmtd
, fdstg
, stat
;
478 char *mtd
= nvram_find_mtd();
479 char buf
[nvram_part_size
];
483 if( (mtd
!= NULL
) && (nvram_part_size
> 0) )
485 if( (fdmtd
= open(mtd
, O_RDONLY
)) > -1 )
487 if( read(fdmtd
, buf
, sizeof(buf
)) == sizeof(buf
) )
489 if((fdstg
= open(NVRAM_STAGING
, O_WRONLY
| O_CREAT
, 0600)) > -1)
491 write(fdstg
, buf
, sizeof(buf
));
507 /* Copy staging file to NVRAM device. */
508 int staging_to_nvram(void)
510 int fdmtd
, fdstg
, stat
;
511 char *mtd
= nvram_find_mtd();
512 char buf
[nvram_part_size
];
516 if( (mtd
!= NULL
) && (nvram_part_size
> 0) )
518 if( (fdstg
= open(NVRAM_STAGING
, O_RDONLY
)) > -1 )
520 if( read(fdstg
, buf
, sizeof(buf
)) == sizeof(buf
) )
522 if( (fdmtd
= open(mtd
, O_WRONLY
| O_SYNC
)) > -1 )
524 write(fdmtd
, buf
, sizeof(buf
));
534 stat
= unlink(NVRAM_STAGING
) ? 1 : 0;