diff --git a/Documentation/user/variables.rst b/Documentation/user/variables.rst index 0abc25d..89aadf5 100644 --- a/Documentation/user/variables.rst +++ b/Documentation/user/variables.rst @@ -66,6 +66,24 @@ model: myboard barebox@yourboard:/ +Non volatile device variables +----------------------------- + +Non volatile device variables are used to make device parameters persistent. They +are regular nv variables but are linked with other devices instead of the global +device. Every nv variable in the form nv.dev.. will be mirrored +to the corresponding . variable. + +This example changes the partitioning of the nand0 device: + +.. code-block:: sh + barebox@Phytec phyCARD-i.MX27:/ nv dev.nand0.partitions: 4M(barebox),1M(barebox-environment),-(root) + barebox@Phytec phyCARD-i.MX27:/ devinfo nand0 + Parameters: + [...] + partitions: 4M(barebox),1M(barebox-environment),8M(kernel),1011M(root) + [...] + .. _magicvars: Magic variables diff --git a/common/globalvar.c b/common/globalvar.c index 75e4d43..c0958e1 100644 --- a/common/globalvar.c +++ b/common/globalvar.c @@ -68,22 +68,115 @@ return 0; } +/** + * dev_param_init_from_nv - initialize a device parameter from nv variable + * @dev: The device + * @name: The parameter name + * + * This function initializes a newly created device parameter from the corresponding + * nv.dev.. variable. + */ +void dev_param_init_from_nv(struct device_d *dev, const char *name) +{ + char *nvname; + const char *val; + int ret = 0; + + if (dev == &nv_device) + return; + if (dev == &global_device) + return; + + nvname = basprintf("dev.%s.%s", dev_name(dev), name); + val = dev_get_param(&nv_device, nvname); + if (val) { + ret = dev_set_param(dev, name, val); + if (ret) + pr_err("Cannot init param from nv: %s.%s=%s: %s\n", + dev_name(dev), name, val, strerror(-ret)); + } + + free(nvname); +} + +/** + * nvvar_device_dispatch - dispatch dev.. name into device and parameter name + * @name: The incoming name in the form dev.. + * @dev: The returned device_d * belonging to + * @pname: the parameter name + * + * Given a dev.. string this function finds the device_d * belonging to + * and the parameter name from . + * + * Return: When incoming string does not belong to the device namespace (does not begin + * with "dev." this function returns 0. A value > 0 is returned when the incoming string + * is in the device namespace and the string can be dispatched into a device_d * and a + * parameter name. A negative error code is returned when the incoming string belongs to + * the device namespace, but cannot be dispatched. + */ +static int nvvar_device_dispatch(const char *name, struct device_d **dev, + const char **pname) +{ + char *devname; + const char *dot; + int dotpos; + + *dev = NULL; + + if (strncmp(name, "dev.", 4)) + return 0; + + name += 4; + + dot = strchr(name, '.'); + if (!dot) + return -EINVAL; + + dotpos = dot - name; + + devname = xstrndup(name, dotpos); + *dev = get_device_by_name(devname); + free(devname); + + if (*dev == &nv_device || *dev == &global_device) + return -EINVAL; + + *pname = dot + 1; + + return 1; +} + static int nv_set(struct device_d *dev, struct param_d *p, const char *val) { - struct param_d *gp; int ret; + int devspace; + struct device_d *rdev; + const char *pname; if (!val) val = ""; - gp = get_param_by_name(&global_device, p->name); - if (!gp) - return -EINVAL; - - ret = gp->set(&global_device, gp, val); - if (ret) + ret = nvvar_device_dispatch(p->name, &rdev, &pname); + if (ret < 0) return ret; + devspace = ret; + + if (devspace) { + if (rdev) { + ret = dev_set_param(rdev, pname, val); + if (ret) { + pr_err("Cannot init param from nv: %s.%s=%s: %s\n", + dev_name(rdev), pname, val, strerror(-ret)); + return ret; + } + } + } else { + ret = dev_set_param(&global_device, p->name, val); + if (ret) + return ret; + } + free(p->value); p->value = xstrdup(val); @@ -99,15 +192,26 @@ { struct param_d *p, *gp; int ret; + int devspace; + struct device_d *dev; + const char *pname; if (!IS_ENABLED(CONFIG_NVVAR)) return -ENOSYS; + ret = nvvar_device_dispatch(name, &dev, &pname); + if (ret < 0) + return ret; + + devspace = ret; + gp = get_param_by_name(&nv_device, name); if (gp) { - ret = dev_set_param(&global_device, name, value); - if (ret) - return ret; + if (!devspace) { + ret = dev_set_param(&global_device, name, value); + if (ret) + return ret; + } ret = dev_set_param(&nv_device, name, value); if (ret) @@ -116,27 +220,26 @@ return 0; } - ret = globalvar_add_simple(name, value); - if (ret && ret != -EEXIST) - return ret; + if (!devspace) { + ret = globalvar_add_simple(name, value); + if (ret && ret != -EEXIST) + return ret; + } p = dev_add_param(&nv_device, name, nv_set, nv_get, 0); if (IS_ERR(p)) return PTR_ERR(p); - if (value) { - ret = dev_set_param(&global_device, name, value); - if (ret) - return ret; - } else { - value = dev_get_param(&global_device, name); - if (!value) - value = ""; + if (!value) { + if (devspace) { + if (dev) + value = dev_get_param(dev, pname); + } else { + value = dev_get_param(&global_device, name); + } } - p->value = xstrdup(value); - - return nv_save(p->name, value); + return nv_set(&nv_device, p, value); } int nvvar_remove(const char *name) diff --git a/include/globalvar.h b/include/globalvar.h index 5dfa371..67b97de 100644 --- a/include/globalvar.h +++ b/include/globalvar.h @@ -94,6 +94,8 @@ int nvvar_remove(const char *name); void globalvar_print(void); +void dev_param_init_from_nv(struct device_d *dev, const char *name); + #else static inline int globalvar_add_simple(const char *name, const char *value) { @@ -165,6 +167,10 @@ return 0; } +static inline void dev_param_init_from_nv(struct device_d *dev, const char *name) +{ +} + #endif #endif /* __GLOBALVAR_H */ diff --git a/lib/parameter.c b/lib/parameter.c index 3d356fb..656a603 100644 --- a/lib/parameter.c +++ b/lib/parameter.c @@ -28,6 +28,7 @@ #include #include #include +#include #include struct param_d *get_param_by_name(struct device_d *dev, const char *name) @@ -156,6 +157,8 @@ param->dev = dev; list_add_sort(¶m->list, &dev->parameters, compare); + dev_param_init_from_nv(dev, name); + return 0; }