new upstream release 2.51 with some fixes, see
[openwrt.git] / package / nvram / src / nvram.c
1 /*
2 * NVRAM variable manipulation (common)
3 *
4 * Copyright 2004, Broadcom Corporation
5 * Copyright 2009, OpenWrt.org
6 * All Rights Reserved.
7 *
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.
12 *
13 */
14
15 #include "nvram.h"
16
17 #define TRACE(msg) \
18 printf("%s(%i) in %s(): %s\n", \
19 __FILE__, __LINE__, __FUNCTION__, msg ? msg : "?")
20
21 size_t nvram_erase_size = 0;
22
23
24 /*
25 * -- Helper functions --
26 */
27
28 /* String hash */
29 static uint32_t hash(const char *s)
30 {
31 uint32_t hash = 0;
32
33 while (*s)
34 hash = 31 * hash + *s++;
35
36 return hash;
37 }
38
39 /* Free all tuples. */
40 static void _nvram_free(nvram_handle_t *h)
41 {
42 uint32_t i;
43 nvram_tuple_t *t, *next;
44
45 /* Free hash table */
46 for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) {
47 for (t = h->nvram_hash[i]; t; t = next) {
48 next = t->next;
49 free(t);
50 }
51 h->nvram_hash[i] = NULL;
52 }
53
54 /* Free dead table */
55 for (t = h->nvram_dead; t; t = next) {
56 next = t->next;
57 free(t);
58 }
59
60 h->nvram_dead = NULL;
61 }
62
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 )
66 {
67 if ((strlen(value) + 1) > NVRAM_SPACE)
68 return NULL;
69
70 if (!t) {
71 if (!(t = malloc(sizeof(nvram_tuple_t) + strlen(name) + 1)))
72 return NULL;
73
74 /* Copy name */
75 t->name = (char *) &t[1];
76 strcpy(t->name, name);
77
78 t->value = NULL;
79 }
80
81 /* Copy value */
82 if (!t->value || strcmp(t->value, value))
83 {
84 if(!(t->value = (char *) realloc(t->value, strlen(value)+1)))
85 return NULL;
86
87 strcpy(t->value, value);
88 t->value[strlen(value)] = '\0';
89 }
90
91 return t;
92 }
93
94 /* (Re)initialize the hash table. */
95 static int _nvram_rehash(nvram_handle_t *h)
96 {
97 nvram_header_t *header = nvram_header(h);
98 char buf[] = "0xXXXXXXXX", *name, *value, *eq;
99
100 /* (Re)initialize hash table */
101 _nvram_free(h);
102
103 /* Parse and set "name=value\0 ... \0\0" */
104 name = (char *) &header[1];
105
106 for (; *name; name = value + strlen(value) + 1) {
107 if (!(eq = strchr(name, '=')))
108 break;
109 *eq = '\0';
110 value = eq + 1;
111 nvram_set(h, name, value);
112 *eq = '=';
113 }
114
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);
119 }
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);
123 }
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);
128 }
129 if (!nvram_get(h, "sdram_ncdl")) {
130 sprintf(buf, "0x%08X", header->config_ncdl);
131 nvram_set(h, "sdram_ncdl", buf);
132 }
133
134 return 0;
135 }
136
137
138 /*
139 * -- Public functions --
140 */
141
142 /* Get nvram header. */
143 nvram_header_t * nvram_header(nvram_handle_t *h)
144 {
145 return (nvram_header_t *) &h->mmap[NVRAM_START(nvram_erase_size)];
146 }
147
148 /* Get the value of an NVRAM variable. */
149 char * nvram_get(nvram_handle_t *h, const char *name)
150 {
151 uint32_t i;
152 nvram_tuple_t *t;
153 char *value;
154
155 if (!name)
156 return NULL;
157
158 /* Hash the name */
159 i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash);
160
161 /* Find the associated tuple in the hash table */
162 for (t = h->nvram_hash[i]; t && strcmp(t->name, name); t = t->next);
163
164 value = t ? t->value : NULL;
165
166 return value;
167 }
168
169 /* Set the value of an NVRAM variable. */
170 int nvram_set(nvram_handle_t *h, const char *name, const char *value)
171 {
172 uint32_t i;
173 nvram_tuple_t *t, *u, **prev;
174
175 /* Hash the name */
176 i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash);
177
178 /* Find the associated tuple in the hash table */
179 for (prev = &h->nvram_hash[i], t = *prev;
180 t && strcmp(t->name, name); prev = &t->next, t = *prev);
181
182 /* (Re)allocate tuple */
183 if (!(u = _nvram_realloc(h, t, name, value)))
184 return -12; /* -ENOMEM */
185
186 /* Value reallocated */
187 if (t && t == u)
188 return 0;
189
190 /* Move old tuple to the dead table */
191 if (t) {
192 *prev = t->next;
193 t->next = h->nvram_dead;
194 h->nvram_dead = t;
195 }
196
197 /* Add new tuple to the hash table */
198 u->next = h->nvram_hash[i];
199 h->nvram_hash[i] = u;
200
201 return 0;
202 }
203
204 /* Unset the value of an NVRAM variable. */
205 int nvram_unset(nvram_handle_t *h, const char *name)
206 {
207 uint32_t i;
208 nvram_tuple_t *t, **prev;
209
210 if (!name)
211 return 0;
212
213 /* Hash the name */
214 i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash);
215
216 /* Find the associated tuple in the hash table */
217 for (prev = &h->nvram_hash[i], t = *prev;
218 t && strcmp(t->name, name); prev = &t->next, t = *prev);
219
220 /* Move it to the dead table */
221 if (t) {
222 *prev = t->next;
223 t->next = h->nvram_dead;
224 h->nvram_dead = t;
225 }
226
227 return 0;
228 }
229
230 /* Get all NVRAM variables. */
231 nvram_tuple_t * nvram_getall(nvram_handle_t *h)
232 {
233 int i;
234 nvram_tuple_t *t, *l, *x;
235
236 l = NULL;
237
238 for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) {
239 for (t = h->nvram_hash[i]; t; t = t->next) {
240 if( (x = (nvram_tuple_t *) malloc(sizeof(nvram_tuple_t))) != NULL )
241 {
242 x->name = t->name;
243 x->value = t->value;
244 x->next = l;
245 l = x;
246 }
247 else
248 {
249 break;
250 }
251 }
252 }
253
254 return l;
255 }
256
257 /* Regenerate NVRAM. */
258 int nvram_commit(nvram_handle_t *h)
259 {
260 nvram_header_t *header = nvram_header(h);
261 char *init, *config, *refresh, *ncdl;
262 char *ptr, *end;
263 int i;
264 nvram_tuple_t *t;
265 nvram_header_t tmp;
266 uint8_t crc;
267
268 /* Regenerate header */
269 header->magic = NVRAM_MAGIC;
270 header->crc_ver_init = (NVRAM_VERSION << 8);
271 if (!(init = nvram_get(h, "sdram_init")) ||
272 !(config = nvram_get(h, "sdram_config")) ||
273 !(refresh = nvram_get(h, "sdram_refresh")) ||
274 !(ncdl = nvram_get(h, "sdram_ncdl"))) {
275 header->crc_ver_init |= SDRAM_INIT << 16;
276 header->config_refresh = SDRAM_CONFIG;
277 header->config_refresh |= SDRAM_REFRESH << 16;
278 header->config_ncdl = 0;
279 } else {
280 header->crc_ver_init |= (strtoul(init, NULL, 0) & 0xffff) << 16;
281 header->config_refresh = strtoul(config, NULL, 0) & 0xffff;
282 header->config_refresh |= (strtoul(refresh, NULL, 0) & 0xffff) << 16;
283 header->config_ncdl = strtoul(ncdl, NULL, 0);
284 }
285
286 /* Clear data area */
287 ptr = (char *) header + sizeof(nvram_header_t);
288 memset(ptr, 0xFF, NVRAM_SPACE - sizeof(nvram_header_t));
289 memset(&tmp, 0, sizeof(nvram_header_t));
290
291 /* Leave space for a double NUL at the end */
292 end = (char *) header + NVRAM_SPACE - 2;
293
294 /* Write out all tuples */
295 for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) {
296 for (t = h->nvram_hash[i]; t; t = t->next) {
297 if ((ptr + strlen(t->name) + 1 + strlen(t->value) + 1) > end)
298 break;
299 ptr += sprintf(ptr, "%s=%s", t->name, t->value) + 1;
300 }
301 }
302
303 /* End with a double NULL and pad to 4 bytes */
304 *ptr = '\0';
305 ptr++;
306
307 if( (int)ptr % 4 )
308 memset(ptr, 0, 4 - ((int)ptr % 4));
309
310 ptr++;
311
312 /* Set new length */
313 header->len = NVRAM_ROUNDUP(ptr - (char *) header, 4);
314
315 /* Little-endian CRC8 over the last 11 bytes of the header */
316 tmp.crc_ver_init = header->crc_ver_init;
317 tmp.config_refresh = header->config_refresh;
318 tmp.config_ncdl = header->config_ncdl;
319 crc = hndcrc8((unsigned char *) &tmp + NVRAM_CRC_START_POSITION,
320 sizeof(nvram_header_t) - NVRAM_CRC_START_POSITION, 0xff);
321
322 /* Continue CRC8 over data bytes */
323 crc = hndcrc8((unsigned char *) &header[0] + sizeof(nvram_header_t),
324 header->len - sizeof(nvram_header_t), crc);
325
326 /* Set new CRC8 */
327 header->crc_ver_init |= crc;
328
329 /* Write out */
330 msync(h->mmap, h->length, MS_SYNC);
331 fsync(h->fd);
332
333 /* Reinitialize hash table */
334 return _nvram_rehash(h);
335 }
336
337 /* Open NVRAM and obtain a handle. */
338 nvram_handle_t * nvram_open(const char *file, int rdonly)
339 {
340 int fd;
341 char *mtd = NULL;
342 nvram_handle_t *h;
343 nvram_header_t *header;
344
345 /* If erase size or file are undefined then try to define them */
346 if( (nvram_erase_size == 0) || (file == NULL) )
347 {
348 /* Finding the mtd will set the appropriate erase size */
349 if( (mtd = nvram_find_mtd()) == NULL || nvram_erase_size == 0 )
350 {
351 free(mtd);
352 return NULL;
353 }
354 }
355
356 if( (fd = open(file ? file : mtd, O_RDWR)) > -1 )
357 {
358 char *mmap_area = (char *) mmap(
359 NULL, nvram_erase_size, PROT_READ | PROT_WRITE,
360 ( rdonly == NVRAM_RO ) ? MAP_PRIVATE : MAP_SHARED, fd, 0);
361
362 if( mmap_area != MAP_FAILED )
363 {
364 memset(mmap_area, 0xFF, NVRAM_START(nvram_erase_size));
365
366 if((h = (nvram_handle_t *) malloc(sizeof(nvram_handle_t))) != NULL)
367 {
368 memset(h, 0, sizeof(nvram_handle_t));
369
370 h->fd = fd;
371 h->mmap = mmap_area;
372 h->length = nvram_erase_size;
373
374 header = nvram_header(h);
375
376 if( header->magic == NVRAM_MAGIC )
377 {
378 _nvram_rehash(h);
379 free(mtd);
380 return h;
381 }
382 else
383 {
384 munmap(h->mmap, h->length);
385 free(h);
386 }
387 }
388 }
389 }
390
391 free(mtd);
392 return NULL;
393 }
394
395 /* Close NVRAM and free memory. */
396 int nvram_close(nvram_handle_t *h)
397 {
398 _nvram_free(h);
399 munmap(h->mmap, h->length);
400 close(h->fd);
401 free(h);
402
403 return 0;
404 }
405
406 /* Determine NVRAM device node. */
407 char * nvram_find_mtd(void)
408 {
409 FILE *fp;
410 int i, esz;
411 char dev[PATH_MAX];
412 char *path = NULL;
413 struct stat s;
414 int supported = 1;
415
416 /* Refuse any operation on the WGT634U */
417 if( (fp = fopen("/proc/diag/model", "r")) )
418 {
419 if( fgets(dev, sizeof(dev), fp) && !strncmp(dev, "Netgear WGT634U", 15) )
420 supported = 0;
421
422 fclose(fp);
423 }
424
425 if( supported && (fp = fopen("/proc/mtd", "r")) )
426 {
427 while( fgets(dev, sizeof(dev), fp) )
428 {
429 if( strstr(dev, "nvram") && sscanf(dev, "mtd%d: %08x", &i, &esz) )
430 {
431 nvram_erase_size = esz;
432
433 sprintf(dev, "/dev/mtdblock/%d", i);
434 if( stat(dev, &s) > -1 && (s.st_mode & S_IFBLK) )
435 {
436 if( (path = (char *) malloc(strlen(dev)+1)) != NULL )
437 {
438 strncpy(path, dev, strlen(dev)+1);
439 break;
440 }
441 }
442 else
443 {
444 sprintf(dev, "/dev/mtdblock%d", i);
445 if( stat(dev, &s) > -1 && (s.st_mode & S_IFBLK) )
446 {
447 if( (path = (char *) malloc(strlen(dev)+1)) != NULL )
448 {
449 strncpy(path, dev, strlen(dev)+1);
450 break;
451 }
452 }
453 }
454 }
455 }
456 fclose(fp);
457 }
458
459 return path;
460 }
461
462 /* Check NVRAM staging file. */
463 char * nvram_find_staging(void)
464 {
465 struct stat s;
466
467 if( (stat(NVRAM_STAGING, &s) > -1) && (s.st_mode & S_IFREG) )
468 {
469 return NVRAM_STAGING;
470 }
471
472 return NULL;
473 }
474
475 /* Copy NVRAM contents to staging file. */
476 int nvram_to_staging(void)
477 {
478 int fdmtd, fdstg, stat;
479 char *mtd = nvram_find_mtd();
480 char buf[nvram_erase_size];
481
482 stat = -1;
483
484 if( (mtd != NULL) && (nvram_erase_size > 0) )
485 {
486 if( (fdmtd = open(mtd, O_RDONLY)) > -1 )
487 {
488 if( read(fdmtd, buf, sizeof(buf)) == sizeof(buf) )
489 {
490 if((fdstg = open(NVRAM_STAGING, O_WRONLY | O_CREAT, 0600)) > -1)
491 {
492 write(fdstg, buf, sizeof(buf));
493 fsync(fdstg);
494 close(fdstg);
495
496 stat = 0;
497 }
498 }
499
500 close(fdmtd);
501 }
502 }
503
504 free(mtd);
505 return stat;
506 }
507
508 /* Copy staging file to NVRAM device. */
509 int staging_to_nvram(void)
510 {
511 int fdmtd, fdstg, stat;
512 char *mtd = nvram_find_mtd();
513 char buf[nvram_erase_size];
514
515 stat = -1;
516
517 if( (mtd != NULL) && (nvram_erase_size > 0) )
518 {
519 if( (fdstg = open(NVRAM_STAGING, O_RDONLY)) > -1 )
520 {
521 if( read(fdstg, buf, sizeof(buf)) == sizeof(buf) )
522 {
523 if( (fdmtd = open(mtd, O_WRONLY | O_SYNC)) > -1 )
524 {
525 write(fdmtd, buf, sizeof(buf));
526 fsync(fdmtd);
527 close(fdmtd);
528 stat = 0;
529 }
530 }
531
532 close(fdstg);
533
534 if( !stat )
535 stat = unlink(NVRAM_STAGING) ? 1 : 0;
536 }
537 }
538
539 free(mtd);
540 return stat;
541 }
This page took 0.075192 seconds and 5 git commands to generate.