diff options
author | Marc Kleine-Budde <mkl@pengutronix.de> | 2015-04-22 14:51:18 +0200 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2015-04-28 15:13:37 +0200 |
commit | 603ac2332db3c5ec6911bdcf62d4e079a92b86e2 (patch) | |
tree | fb27cfa27b3a06f26baf6c493da832e52230e74c /src | |
parent | d5025409ebccc4f39ffe9dc939ccd324f81711d5 (diff) | |
download | dt-utils-603ac2332db3c5ec6911bdcf62d4e079a92b86e2.tar.gz dt-utils-603ac2332db3c5ec6911bdcf62d4e079a92b86e2.tar.xz |
barebox-state: import from barebox
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Diffstat (limited to 'src')
-rw-r--r-- | src/barebox-state.c | 1138 |
1 files changed, 727 insertions, 411 deletions
diff --git a/src/barebox-state.c b/src/barebox-state.c index 75af097..2be147c 100644 --- a/src/barebox-state.c +++ b/src/barebox-state.c @@ -25,6 +25,7 @@ #include <fcntl.h> #include <stdio.h> #include <dt.h> +#include <fdt.h> #include <asm/byteorder.h> #include <linux/types.h> @@ -35,16 +36,41 @@ static int verbose; -enum state_variable_type { - STATE_TYPE_INVALID = 0, - STATE_TYPE_ENUM, - STATE_TYPE_U32, - STATE_TYPE_MAC, -}; +struct state_variable; + +static int state_uint32_set(struct state_variable *var, const char *val); +static char *state_uint32_get(struct state_variable *var); +static int state_enum32_set(struct state_variable *sv, const char *val); +static char *state_enum32_get(struct state_variable *var); +static void state_enum32_info(struct state_variable *var); +static int state_mac_set(struct state_variable *var, const char *val); +static char *state_mac_get(struct state_variable *var); + +#define asprintf(fmt, arg...) barebox_asprintf(fmt, ##arg) + +char *barebox_asprintf(const char *fmt, ...) __attribute__ ((format(__printf__, 1, 2))); +char *barebox_asprintf(const char *fmt, ...) +{ + va_list ap; + char *p; + int ret; + + va_start(ap, fmt); + ret = vasprintf(&p, fmt, ap); + va_end(ap); + + return ret == -1 ? NULL : p; +} + +/* ------------------------------------------------------------ */ + +#define RAW_BACKEND_COPIES 2 struct state_backend; struct state { + struct device_d dev; + const struct device_node *root; struct list_head variables; const char *name; struct list_head list; @@ -57,20 +83,24 @@ struct state_backend { int (*load)(struct state_backend *backend, struct state *state); int (*save)(struct state_backend *backend, struct state *state); const char *name; - char *path; + const char *path; }; -/* list of all registered state instances */ -static LIST_HEAD(state_list); +enum state_variable_type { + STATE_TYPE_INVALID = 0, + STATE_TYPE_ENUM, + STATE_TYPE_U32, + STATE_TYPE_MAC, +}; /* instance of a single variable */ struct state_variable { enum state_variable_type type; struct list_head list; - char *name; + const char *name; + unsigned int start; + unsigned int size; void *raw; - int raw_size; - int index; }; /* A variable type (uint32, enum32) */ @@ -79,7 +109,7 @@ struct variable_type { const char *type_name; struct list_head list; int (*export)(struct state_variable *, struct device_node *); - int (*init)(struct state_variable *, struct device_node *); + int (*import)(struct state_variable *, const struct device_node *); struct state_variable *(*create)(struct state *state, const char *name, struct device_node *); char *(*get)(struct state_variable *); @@ -87,6 +117,18 @@ struct variable_type { void (*info)(struct state_variable *); }; +/* list of all registered state instances */ +static LIST_HEAD(state_list); + +static int state_set_dirty(struct param_d *p, void *priv) +{ + struct state *state = priv; + + state->dirty = 1; + + return 0; +} + /* * uint32 */ @@ -102,17 +144,12 @@ static int state_var_compare(struct list_head *a, struct list_head *b) struct state_variable *va = list_entry(a, struct state_variable, list); struct state_variable *vb = list_entry(b, struct state_variable, list); - if (va->index == vb->index) - return 0; - - return va->index < vb->index ? -1 : 1; + return va->start < vb->start ? -1 : 1; } -static int state_add_variable(struct state *state, struct state_variable *var) +static void state_add_var(struct state *state, struct state_variable *var) { list_add_sort(&var->list, &state->variables, state_var_compare); - - return 0; } static inline struct state_uint32 *to_state_uint32(struct state_variable *s) @@ -126,33 +163,23 @@ static int state_uint32_export(struct state_variable *var, struct state_uint32 *su32 = to_state_uint32(var); int ret; - ret = of_property_write_u32(node, "default", su32->value_default); - if (ret) - return ret; + if (su32->value_default) { + ret = of_property_write_u32(node, "default", + su32->value_default); + if (ret) + return ret; + } return of_property_write_u32(node, "value", su32->value); } -static int state_uint32_init(struct state_variable *sv, - struct device_node *node) +static int state_uint32_import(struct state_variable *sv, + const struct device_node *node) { - int len; - const __be32 *value, *value_default; struct state_uint32 *su32 = to_state_uint32(sv); - value_default = of_get_property(node, "default", &len); - if (value_default && len != sizeof(uint32_t)) - return -EINVAL; - - value = of_get_property(node, "value", &len); - if (value && len != sizeof(uint32_t)) - return -EINVAL; - - if (value_default) - su32->value_default = be32_to_cpu(*value_default); - if (value) - su32->value = be32_to_cpu(*value); - else + of_property_read_u32(node, "default", &su32->value_default); + if (of_property_read_u32(node, "value", &su32->value)) su32->value = su32->value_default; return 0; @@ -162,39 +189,23 @@ static struct state_variable *state_uint32_create(struct state *state, const char *name, struct device_node *node) { struct state_uint32 *su32; + struct param_d *param; su32 = xzalloc(sizeof(*su32)); - pr_debug("%s: %s\n", __func__, name); + param = dev_add_param_int(&state->dev, name, state_set_dirty, + NULL, (int *)&su32->value, "%d", state); + if (IS_ERR(param)) { + free(su32); + return ERR_CAST(param); + } - su32->var.raw_size = sizeof(uint32_t); + su32->var.size = sizeof(uint32_t); su32->var.raw = &su32->value; return &su32->var; } -static int state_uint32_set(struct state_variable *var, const char *val) -{ - struct state_uint32 *su32 = to_state_uint32(var); - - su32->value = strtoul(val, NULL, 0); - - return 0; -} - -static char *state_uint32_get(struct state_variable *var) -{ - struct state_uint32 *su32 = to_state_uint32(var); - char *str; - int ret; - - ret = asprintf(&str, "%d", su32->value); - if (ret < 0) - return ERR_PTR(-ENOMEM); - - return str; -} - /* * enum32 */ @@ -219,9 +230,12 @@ static int state_enum32_export(struct state_variable *var, int ret, i, len; char *prop, *str; - ret = of_property_write_u32(node, "default", enum32->value_default); - if (ret) - return ret; + if (enum32->value_default) { + ret = of_property_write_u32(node, "default", + enum32->value_default); + if (ret) + return ret; + } ret = of_property_write_u32(node, "value", enum32->value); if (ret) @@ -245,7 +259,8 @@ static int state_enum32_export(struct state_variable *var, return ret; } -static int state_enum32_init(struct state_variable *sv, struct device_node *node) +static int state_enum32_import(struct state_variable *sv, + const struct device_node *node) { struct state_enum32 *enum32 = to_state_enum32(sv); int len; @@ -281,7 +296,7 @@ static struct state_variable *state_enum32_create(struct state *state, enum32->names = xzalloc(sizeof(char *) * num_names); enum32->num_names = num_names; - enum32->var.raw_size = sizeof(uint32_t); + enum32->var.size = sizeof(uint32_t); enum32->var.raw = &enum32->value; for (i = 0; i < num_names; i++) { @@ -290,59 +305,25 @@ static struct state_variable *state_enum32_create(struct state *state, ret = of_property_read_string_index(node, "names", i, &name); if (ret) goto out; - enum32->names[i] = strdup(name); + enum32->names[i] = xstrdup(name); } - pr_debug("%s: %s\n", __func__, name); + enum32->param = dev_add_param_enum(&state->dev, name, state_set_dirty, + NULL, (int *)&enum32->value, enum32->names, num_names, state); + if (IS_ERR(enum32->param)) { + ret = PTR_ERR(enum32->param); + goto out; + } return &enum32->var; out: + for (i--; i >= 0; i--) + free((char *)enum32->names[i]); free(enum32->names); free(enum32); return ERR_PTR(ret); } -static int state_enum32_set(struct state_variable *sv, const char *val) -{ - struct state_enum32 *enum32 = to_state_enum32(sv); - int i; - - for (i = 0; i < enum32->num_names; i++) { - if (!strcmp(enum32->names[i], val)) { - enum32->value = i; - return 0; - } - } - - return -EINVAL; -} - -static char *state_enum32_get(struct state_variable *var) -{ - struct state_enum32 *enum32 = to_state_enum32(var); - char *str; - int ret; - - ret = asprintf(&str, "%s", enum32->names[enum32->value]); - if (ret < 0) - return ERR_PTR(-ENOMEM); - - return str; -} - -static void state_enum32_info(struct state_variable *var) -{ - struct state_enum32 *enum32 = to_state_enum32(var); - int i; - - printf(", values=["); - - for (i = 0; i < enum32->num_names; i++) - printf("%s%s", enum32->names[i], - i == enum32->num_names - 1 ? "" : ","); - printf("]"); -} - /* * MAC address */ @@ -364,26 +345,25 @@ static int state_mac_export(struct state_variable *var, struct state_mac *mac = to_state_mac(var); int ret; - ret = of_property_write_u8_array(node, "default", mac->value_default, 6); + ret = of_property_write_u8_array(node, "default", mac->value_default, + ARRAY_SIZE(mac->value_default)); if (ret) return ret; - return of_property_write_u8_array(node, "value", mac->value, 6); + return of_property_write_u8_array(node, "value", mac->value, + ARRAY_SIZE(mac->value)); } -static int state_mac_init(struct state_variable *sv, struct device_node *node) +static int state_mac_import(struct state_variable *sv, + const struct device_node *node) { struct state_mac *mac = to_state_mac(sv); - uint8_t value[6] = {}; - uint8_t value_default[6] = {}; - of_property_read_u8_array(node, "default", value_default, 6); - memcpy(mac->value_default, value_default, 6); - - if (!of_property_read_u8_array(node, "value", value, 6)) - memcpy(mac->value, value, 6); - else - memcpy(mac->value, value_default, 6); + of_property_read_u8_array(node, "default", mac->value_default, + ARRAY_SIZE(mac->value_default)); + if (of_property_read_u8_array(node, "value", mac->value, + ARRAY_SIZE(mac->value))) + memcpy(mac->value, mac->value_default, ARRAY_SIZE(mac->value)); return 0; } @@ -396,10 +376,15 @@ static struct state_variable *state_mac_create(struct state *state, mac = xzalloc(sizeof(*mac)); - mac->var.raw_size = 6; + mac->var.size = ARRAY_SIZE(mac->value); mac->var.raw = mac->value; - pr_debug("%s: %s\n", __func__, name); + mac->param = dev_add_param_mac(&state->dev, name, state_set_dirty, + NULL, mac->value, state); + if (IS_ERR(mac->param)) { + ret = PTR_ERR(mac->param); + goto out; + } return &mac->var; out: @@ -407,64 +392,12 @@ out: return ERR_PTR(ret); } -int string_to_ethaddr(const char *str, uint8_t enetaddr[6]) -{ - int reg; - char *e; - - if (!str || strlen(str) != 17) { - memset(enetaddr, 0, 6); - return -EINVAL; - } - - if (str[2] != ':' || str[5] != ':' || str[8] != ':' || - str[11] != ':' || str[14] != ':') - return -EINVAL; - - for (reg = 0; reg < 6; ++reg) { - enetaddr[reg] = strtoul(str, &e, 16); - str = e + 1; - } - - return 0; -} - -static int state_mac_set(struct state_variable *var, const char *val) -{ - struct state_mac *mac = to_state_mac(var); - char mac_save[6]; - int ret; - - ret = string_to_ethaddr(val, mac_save); - if (ret) - return ret; - - memcpy(mac->value, mac_save, 6); - - return 0; -} - -static char *state_mac_get(struct state_variable *var) -{ - struct state_mac *mac = to_state_mac(var); - char *str; - int ret; - - ret = asprintf(&str, "%02x:%02x:%02x:%02x:%02x:%02x", - mac->value[0], mac->value[1], mac->value[2], - mac->value[3], mac->value[4], mac->value[5]); - if (ret < 0) - return ERR_PTR(-ENOMEM); - - return str; -} - static struct variable_type types[] = { { .type = STATE_TYPE_U32, .type_name = "uint32", .export = state_uint32_export, - .init = state_uint32_init, + .import = state_uint32_import, .create = state_uint32_create, .set = state_uint32_set, .get = state_uint32_get, @@ -472,7 +405,7 @@ static struct variable_type types[] = { .type = STATE_TYPE_ENUM, .type_name = "enum32", .export = state_enum32_export, - .init = state_enum32_init, + .import = state_enum32_import, .create = state_enum32_create, .set = state_enum32_set, .get = state_enum32_get, @@ -481,26 +414,13 @@ static struct variable_type types[] = { .type = STATE_TYPE_MAC, .type_name = "mac", .export = state_mac_export, - .init = state_mac_init, + .import = state_mac_import, .create = state_mac_create, .set = state_mac_set, .get = state_mac_get, }, }; -static struct variable_type *state_find_type(enum state_variable_type type) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(types); i++) { - if (type == types[i].type) { - return &types[i]; - } - } - - return NULL; -} - static struct variable_type *state_find_type_by_name(const char *name) { int i; @@ -524,9 +444,21 @@ static struct state *state_new(const char *name) int ret; state = xzalloc(sizeof(*state)); - state->name = name; + safe_strncpy(state->dev.name, name, MAX_DRIVER_NAME); + state->name = state->dev.name; + state->dev.id = DEVICE_ID_SINGLE; INIT_LIST_HEAD(&state->variables); + ret = register_device(&state->dev); + if (ret) { + free(state); + return ERR_PTR(ret); + } + + state->dirty = 1; + dev_add_param_bool(&state->dev, "dirty", NULL, NULL, &state->dirty, + NULL); + list_add_tail(&state->list, &state_list); return state; @@ -534,48 +466,13 @@ static struct state *state_new(const char *name) static void state_release(struct state *state) { + list_del(&state->list); + unregister_device(&state->dev); free(state); } -static struct device_node *state_to_node(struct state *state) -{ - struct device_node *root, *node; - struct state_variable *sv; - int ret; - - root = of_new_node(NULL, NULL); - - list_for_each_entry(sv, &state->variables, list) { - struct variable_type *vtype; - char *name; - - asprintf(&name, "%s@%d", sv->name, sv->index); - node = of_new_node(root, name); - free(name); - - vtype = state_find_type(sv->type); - if (!vtype) { - ret = -ENOENT; - goto out; - } - - of_set_property(node, "type", vtype->type_name, - strlen(vtype->type_name) + 1, 1); - - ret = vtype->export(sv, node); - if (ret) - goto out; - } - - of_property_write_u32(root, "magic", state->magic); - - return root; -out: - of_delete_node(root); - return ERR_PTR(ret); -} - -static struct state_variable *state_find_var(struct state *state, const char *name) +static struct state_variable *state_find_var(struct state *state, + const char *name) { struct state_variable *sv; @@ -584,121 +481,218 @@ static struct state_variable *state_find_var(struct state *state, const char *na return sv; } - return NULL; + return ERR_PTR(-ENOENT); } -static char *state_get_var(struct state *state, const char *var) -{ - struct state_variable *sv; - struct variable_type *vtype; - - sv = state_find_var(state, var); - if (!sv) - return NULL; - - vtype = state_find_type(sv->type); - - return vtype->get(sv); -} +enum state_convert { + STATE_CONVERT_FROM_NODE, + STATE_CONVERT_FROM_NODE_CREATE, + STATE_CONVERT_TO_NODE, +}; -static int state_set_var(struct state *state, const char *var, const char *val) +static int state_convert_node_variable(struct state *state, + struct device_node *node, struct device_node *parent, + const char *parent_name, enum state_convert conv) { + const struct variable_type *vtype; + struct device_node *child; + struct device_node *new_node = NULL; struct state_variable *sv; - struct variable_type *vtype; + const char *type_name; + char *short_name, *name, *indexs; + unsigned int start_size[2]; int ret; - sv = state_find_var(state, var); - if (!sv) - return -ENOENT; + /* strip trailing @<ADDRESS> */ + short_name = xstrdup(node->name); + indexs = strchr(short_name, '@'); + if (indexs) + *indexs = 0; - vtype = state_find_type(sv->type); + /* construct full name */ + name = asprintf("%s%s%s", + parent_name, parent_name[0] ? "." : "", short_name); + free(short_name); - if (!vtype->set) - return -EPERM; + if (conv == STATE_CONVERT_TO_NODE) + new_node = of_new_node(parent, node->name); - ret = vtype->set(sv, val); - if (ret) - return ret; - - state->dirty = 1; - - return 0; -} - -static int state_variable_from_node(struct state *state, struct device_node *node, - bool create) -{ - struct variable_type *vtype; - struct state_variable *sv; - char *name, *indexs; - int index = 0; - const char *type_name = NULL; + for_each_child_of_node(node, child) { + ret = state_convert_node_variable(state, child, new_node, name, + conv); + if (ret) + goto out_free; + } - of_property_read_string(node, "type", &type_name); - if (!type_name) - return -EINVAL; + /* parents are allowed to have no type */ + ret = of_property_read_string(node, "type", &type_name); + if (!list_empty(&node->children) && ret == -EINVAL) { + ret = 0; + goto out_free; + } else if (ret) { + goto out_free; + } vtype = state_find_type_by_name(type_name); - if (!vtype) - return -ENOENT; - - name = strdup(node->name); - indexs = strchr(name, '@'); - if (indexs) { - *indexs++ = 0; - index = strtoul(indexs, NULL, 10); + if (!vtype) { + ret = -ENOENT; + goto out_free; } - if (create) { + if (conv == STATE_CONVERT_FROM_NODE_CREATE) { sv = vtype->create(state, name, node); if (IS_ERR(sv)) { - int ret = PTR_ERR(sv); - pr_err("failed to create %s: %s\n", name, strerror(-ret)); - return ret; + ret = PTR_ERR(sv); + dev_err(&state->dev, "failed to create %s: %s\n", + name, strerror(-ret)); + goto out_free; + } + + ret = of_property_read_u32_array(node, "reg", start_size, + ARRAY_SIZE(start_size)); + if (ret) + goto out_free; + + if (start_size[1] != sv->size) { + dev_err(&state->dev, + "size mismatch: type=%s(size=%u) size=%u\n", + type_name, sv->size, start_size[1]); + ret = -EOVERFLOW; + goto out_free; } + sv->name = name; + sv->start = start_size[0]; sv->type = vtype->type; - sv->index = index; - state_add_variable(state, sv); + state_add_var(state, sv); } else { sv = state_find_var(state, name); if (IS_ERR(sv)) { - int ret = PTR_ERR(sv); - pr_err("no such variable: %s: %s\n", name, strerror(-ret)); - return ret; + /* we ignore this error */ + dev_dbg(&state->dev, + "no such variable: %s: %s\n", + name, strerror(-ret)); + ret = 0; + goto out_free; + } + free(name); + + if (conv == STATE_CONVERT_TO_NODE) { + ret = of_set_property(new_node, "type", + vtype->type_name, + strlen(vtype->type_name) + 1, 1); + if (ret) + goto out; + + start_size[0] = sv->start; + start_size[1] = sv->size; + ret = of_property_write_u32_array(new_node, "reg", + start_size, + ARRAY_SIZE(start_size)); + if (ret) + goto out; } } - vtype->init(sv, node); + if (conv == STATE_CONVERT_TO_NODE) + ret = vtype->export(sv, new_node); + else + ret = vtype->import(sv, node); + + if (ret) + goto out; return 0; +out_free: + free(name); +out: + return ret; +} + +static struct device_node *state_to_node(struct state *state) +{ + struct device_node *child; + struct device_node *root; + int ret; + + root = of_new_node(NULL, NULL); + ret = of_property_write_u32(root, "magic", state->magic); + if (ret) + goto out; + + for_each_child_of_node(state->root, child) { + ret = state_convert_node_variable(state, child, root, "", + STATE_CONVERT_TO_NODE); + if (ret) + goto out; + } + + return root; +out: + of_delete_node(root); + return ERR_PTR(ret); } -int state_from_node(struct state *state, struct device_node *node, bool create) +static int state_from_node(struct state *state, struct device_node *node, + bool create) { struct device_node *child; + enum state_convert conv; int ret; uint32_t magic; - of_property_read_u32(node, "magic", &magic); + ret = of_property_read_u32(node, "magic", &magic); + if (ret) + return ret; if (create) { + conv = STATE_CONVERT_FROM_NODE_CREATE; + state->root = node; state->magic = magic; } else { + conv = STATE_CONVERT_FROM_NODE; if (state->magic && state->magic != magic) { - pr_err("invalid magic 0x%08x, should be 0x%08x\n", + dev_err(&state->dev, + "invalid magic 0x%08x, should be 0x%08x\n", magic, state->magic); return -EINVAL; } } for_each_child_of_node(node, child) { - ret = state_variable_from_node(state, child, create); + ret = state_convert_node_variable(state, child, NULL, "", conv); if (ret) return ret; } - return 0; + /* check for overlapping variables */ + if (create) { + const struct state_variable *sv; + + /* start with second entry */ + sv = list_first_entry(&state->variables, + struct state_variable, list); + + list_for_each_entry_continue(sv, &state->variables, list) { + const struct state_variable *last_sv; + + last_sv = list_last_entry(&sv->list, + struct state_variable, list); + if ((last_sv->start + last_sv->size - 1) < sv->start) + continue; + + dev_err(&state->dev, + "ERROR: Conflicting variable position between: " + "%s (0x%02x..0x%02x) and %s (0x%02x..0x%02x)\n", + last_sv->name, last_sv->start, + last_sv->start + last_sv->size - 1, + sv->name, sv->start, sv->start + sv->size - 1); + + ret |= -EINVAL; + } + } + + return ret; } /* @@ -713,8 +707,8 @@ struct state *state_new_from_node(const char *name, struct device_node *node) int ret; state = state_new(name); - if (!state) - return ERR_PTR(-EINVAL); + if (IS_ERR(state)) + return state; ret = state_from_node(state, node, 1); if (ret) { @@ -726,23 +720,69 @@ struct state *state_new_from_node(const char *name, struct device_node *node) } /* + * state_new_from_fdt - create a new state instance from a fdt binary blob + * + * @name The name of the new state instance + * @fdt The fdt binary blob describing the new state instance + */ +struct state *state_new_from_fdt(const char *name, void *fdt) +{ + struct state *state; + struct device_node *root; + + root = of_unflatten_dtb(fdt); + if (!root) + return ERR_PTR(-EINVAL); + + state = state_new_from_node(name, root); + + of_delete_node(root); + + return state; +} + +/* * state_by_name - find a state instance by name * * @name The name of the state instance */ struct state *state_by_name(const char *name) { - struct state *s; + struct state *state; - list_for_each_entry(s, &state_list, list) { - if (!strcmp(name, s->name)) - return s; + list_for_each_entry(state, &state_list, list) { + if (!strcmp(name, state->name)) + return state; } return NULL; } /* + * state_by_node - find a state instance by of node + * + * @node The of node of the state intance + */ +struct state *state_by_node(const struct device_node *node) +{ + struct state *state; + + list_for_each_entry(state, &state_list, list) { + if (state->root == node) + return state; + } + + return NULL; +} + +int state_get_name(const struct state *state, char const **name) +{ + *name = xstrdup(state->name); + + return 0; +} + +/* * state_load - load a state from the backing store * * @state The state instance to load @@ -756,11 +796,11 @@ int state_load(struct state *state) ret = state->backend->load(state->backend, state); if (ret) - return ret; - - state->dirty = 0; + state->dirty = 1; + else + state->dirty = 0; - return 0; + return ret; } /* @@ -772,6 +812,9 @@ int state_save(struct state *state) { int ret; + if (!state->dirty) + return 0; + if (!state->backend) return -ENOSYS; @@ -786,33 +829,144 @@ int state_save(struct state *state) void state_info(void) { - struct state *s; + struct state *state; printf("registered state instances:\n"); - list_for_each_entry(s, &state_list, list) { - printf("%-20s ", s->name); - if (s->backend) - printf("(backend: %s, path: %s)\n", s->backend->name, s->backend->path); + list_for_each_entry(state, &state_list, list) { + printf("%-20s ", state->name); + if (state->backend) + printf("(backend: %s, path: %s)\n", + state->backend->name, state->backend->path); else printf("(no backend)\n"); } } -static int get_meminfo(const char *path, struct mtd_info_user *meminfo) +static int mtd_get_meminfo(const char *path, struct mtd_info_user *meminfo) { int fd, ret; fd = open(path, O_RDWR); if (fd < 0) - return -errno; + return fd; ret = ioctl(fd, MEMGETINFO, meminfo); close(fd); - if (ret) - return -errno; + return ret; +} + +/* + * DTB backend implementation + */ +struct state_backend_dtb { + struct state_backend backend; + bool need_erase; +}; + +static int state_backend_dtb_load(struct state_backend *backend, + struct state *state) +{ + struct device_node *root; + void *fdt; + int ret; + size_t len; + + fdt = read_file(backend->path, &len); + if (!fdt) { + dev_err(&state->dev, "cannot read %s\n", backend->path); + return -EINVAL; + } + + root = of_unflatten_dtb(fdt); + + free(fdt); + + if (IS_ERR(root)) + return PTR_ERR(root); + + ret = state_from_node(state, root, 0); + + return ret; +} + +static int state_backend_dtb_save(struct state_backend *backend, + struct state *state) +{ + struct state_backend_dtb *backend_dtb = container_of(backend, + struct state_backend_dtb, backend); + int ret, fd; + struct device_node *root; + struct fdt_header *fdt; + + root = state_to_node(state); + if (IS_ERR(root)) + return PTR_ERR(root); + + fdt = of_flatten_dtb(root); + if (!fdt) + return -EINVAL; + + fd = open(backend->path, O_WRONLY); + if (fd < 0) { + ret = fd; + goto out; + } + + if (backend_dtb->need_erase) { + ret = erase(fd, fdt32_to_cpu(fdt->totalsize), 0); + if (ret) { + close(fd); + goto out; + } + } + + ret = write_full(fd, fdt, fdt32_to_cpu(fdt->totalsize)); + + close(fd); + + if (ret < 0) + goto out; + + ret = 0; +out: + free(fdt); + of_delete_node(root); + + return ret; +} + +/* + * state_backend_dtb_file - create a dtb backend store for a state instance + * + * @state The state instance to work on + * @path The path where the state will be stored to + */ +int state_backend_dtb_file(struct state *state, const char *path) +{ + struct state_backend_dtb *backend_dtb; + struct state_backend *backend; + struct mtd_info_user meminfo; + int ret; + + if (state->backend) + return -EBUSY; + + backend_dtb = xzalloc(sizeof(*backend_dtb)); + backend = &backend_dtb->backend; + + backend->load = state_backend_dtb_load; + backend->save = state_backend_dtb_save; + backend->path = xstrdup(path); + backend->name = "dtb"; + + state->backend = backend; + + ret = mtd_get_meminfo(backend->path, &meminfo); + if (!ret && !(meminfo.flags & MTD_NO_ERASE)) + backend_dtb->need_erase = true; return 0; } @@ -822,14 +976,13 @@ static int get_meminfo(const char *path, struct mtd_info_user *meminfo) */ struct state_backend_raw { struct state_backend backend; - struct state *state; unsigned long size_data; /* The raw data size (without magic and crc) */ unsigned long size_full; unsigned long step; /* The step in bytes between two copies */ off_t offset; /* offset in the storage file */ size_t size; /* size of the storage area */ - int need_erase; int num_copy_read; /* The first successfully read copy */ + bool need_erase; }; struct backend_raw_header { @@ -841,72 +994,68 @@ struct backend_raw_header { }; static int backend_raw_load_one(struct state_backend_raw *backend_raw, - int fd, off_t offset) + struct state *state, int fd, off_t offset) { - struct state *state = backend_raw->state; uint32_t crc; - void *off; struct state_variable *sv; struct backend_raw_header header = {}; - int ret, len; + int ret; void *buf; ret = lseek(fd, offset, SEEK_SET); if (ret < 0) return ret; - ret = read(fd, &header, sizeof(header)); + ret = read_full(fd, &header, sizeof(header)); if (ret < 0) return ret; - if (ret < sizeof(header)) - return -EINVAL; crc = crc32(0, &header, sizeof(header) - sizeof(uint32_t)); if (crc != header.header_crc) { - pr_err("invalid header crc, calculated 0x%08x, found 0x%08x\n", - crc, header.header_crc); + dev_err(&state->dev, + "invalid header crc, calculated 0x%08x, found 0x%08x\n", + crc, header.header_crc); return -EINVAL; } if (state->magic && state->magic != header.magic) { - pr_err("invalid magic 0x%08x, should be 0x%08x\n", - header.magic, state->magic); + dev_err(&state->dev, + "invalid magic 0x%08x, should be 0x%08x\n", + header.magic, state->magic); return -EINVAL; } buf = xzalloc(header.data_len); - ret = read(fd, buf, header.data_len); + ret = read_full(fd, buf, header.data_len); if (ret < 0) - return ret; - if (ret < header.data_len) - return -EINVAL; + goto out_free; crc = crc32(0, buf, header.data_len); if (crc != header.data_crc) { - pr_err("invalid crc, calculated 0x%08x, found 0x%08x\n", - crc, header.data_crc); - return -EINVAL; + dev_err(&state->dev, + "invalid crc, calculated 0x%08x, found 0x%08x\n", + crc, header.data_crc); + ret = -EINVAL; + goto out_free; } - off = buf; - len = header.data_len; - list_for_each_entry(sv, &state->variables, list) { - if (len < sv->raw_size) { + if (sv->start + sv->size > header.data_len) break; - } - memcpy(sv->raw, off, sv->raw_size); - off += sv->raw_size; - len -= sv->raw_size; + memcpy(sv->raw, buf + sv->start, sv->size); } free(buf); - return 0; + + out_free: + free(buf); + return ret; } -static int state_backend_raw_load(struct state_backend *backend, struct state *state) +static int state_backend_raw_load(struct state_backend *backend, + struct state *state) { struct state_backend_raw *backend_raw = container_of(backend, struct state_backend_raw, backend); @@ -916,13 +1065,14 @@ static int state_backend_raw_load(struct state_backend *backend, struct state *s if (fd < 0) return fd; - for (i = 0; i < 2; i++) { + for (i = 0; i < RAW_BACKEND_COPIES; i++) { off_t offset = backend_raw->offset + i * backend_raw->step; - ret = backend_raw_load_one(backend_raw, fd, offset); + ret = backend_raw_load_one(backend_raw, state, fd, offset); if (!ret) { backend_raw->num_copy_read = i; - pr_debug("copy %d successfully loaded\n", i); + dev_dbg(&state->dev, + "copy %d successfully loaded\n", i); break; } } @@ -933,77 +1083,75 @@ static int state_backend_raw_load(struct state_backend *backend, struct state *s } static int backend_raw_write_one(struct state_backend_raw *backend_raw, - int fd, int num, void *buf, size_t size) + struct state *state, int fd, int num, void *buf, size_t size) { int ret; off_t offset = backend_raw->offset + num * backend_raw->step; - pr_debug("%s: 0x%08lx 0x%08x\n", __func__, offset, size); - - if (backend_raw->need_erase) { - struct erase_info_user erase = { - .start = offset, - .length = backend_raw->step, - }; - - ret = ioctl(fd, MEMERASE, &erase); - if (ret < 0) - return -errno; - } + dev_dbg(&state->dev, "%s: 0x%08lx 0x%08zx\n", + __func__, offset, size); ret = lseek(fd, offset, SEEK_SET); if (ret < 0) - return -errno; + return ret; - ret = write(fd, buf, size); + if (backend_raw->need_erase) { + ret = erase(fd, backend_raw->size_full, offset); + if (ret) + return ret; + } + + ret = write_full(fd, buf, size); if (ret < 0) - return -errno; + return ret; return 0; } -static int state_backend_raw_save(struct state_backend *backend, struct state *state) +static int state_backend_raw_save(struct state_backend *backend, + struct state *state) { struct state_backend_raw *backend_raw = container_of(backend, struct state_backend_raw, backend); int ret = 0, size, fd; - void *buf, *freep, *off, *data, *tmp; + void *buf, *data; struct backend_raw_header *header; struct state_variable *sv; size = backend_raw->size_data + sizeof(struct backend_raw_header); - freep = off = buf = xzalloc(size); + buf = xzalloc(size); header = buf; data = buf + sizeof(*header); - tmp = data; - list_for_each_entry(sv, &state->variables, list) { - memcpy(tmp, sv->raw, sv->raw_size); - tmp += sv->raw_size; - } + list_for_each_entry(sv, &state->variables, list) + memcpy(data + sv->start, sv->raw, sv->size); header->magic = state->magic; header->data_len = backend_raw->size_data; header->data_crc = crc32(0, data, backend_raw->size_data); - header->header_crc = crc32(0, header, sizeof(*header) - sizeof(uint32_t)); + header->header_crc = crc32(0, header, + sizeof(*header) - sizeof(uint32_t)); fd = open(backend->path, O_WRONLY); if (fd < 0) - return fd; + goto out_free; - ret = backend_raw_write_one(backend_raw, fd, !backend_raw->num_copy_read, buf, size); + ret = backend_raw_write_one(backend_raw, state, fd, + !backend_raw->num_copy_read, buf, size); if (ret) - goto out; + goto out_close; - ret = backend_raw_write_one(backend_raw, fd, backend_raw->num_copy_read, buf, size); + ret = backend_raw_write_one(backend_raw, state, fd, + backend_raw->num_copy_read, buf, size); if (ret) - goto out; + goto out_close; - pr_debug("wrote state to %s\n", backend->path); -out: + dev_dbg(&state->dev, "wrote state to %s\n", backend->path); +out_close: close(fd); +out_free: free(buf); return ret; @@ -1017,12 +1165,14 @@ out: * @offset The offset in the storage file * @size The maximum size to use in the storage file * - * This backend stores raw binary data from a state instance. The binary data is - * protected with a magic value which has to match and a crc32 that must be valid. - * Up to four copies are stored if there is sufficient space available. - * @path can be a path to a device or a regular file. When it's a device @size may - * be 0. The four copies are spread to different eraseblocks if approriate for this - * device. + * This backend stores raw binary data from a state instance. The + * binary data is protected with a magic value which has to match and + * a crc32 that must be valid. Two copies are stored, sufficient + * space must be available. + + * @path can be a path to a device or a regular file. When it's a + * device @size may be 0. The two copies are spread to different + * eraseblocks if approriate for this device. */ int state_backend_raw_file(struct state *state, const char *path, off_t offset, size_t size) @@ -1038,7 +1188,7 @@ int state_backend_raw_file(struct state *state, const char *path, off_t offset, return -EBUSY; ret = stat(path, &s); - if (!ret && !S_ISCHR(s.st_mode) && !S_ISBLK(s.st_mode)) { + if (!ret && S_ISCHR(s.st_mode)) { if (size == 0) size = s.st_size; else if (offset + size > s.st_size) @@ -1050,37 +1200,31 @@ int state_backend_raw_file(struct state *state, const char *path, off_t offset, backend->load = state_backend_raw_load; backend->save = state_backend_raw_save; - backend->path = strdup(path); + backend->path = xstrdup(path); backend->name = "raw"; - list_for_each_entry(sv, &state->variables, list) - backend_raw->size_data += sv->raw_size; - - backend_raw->state = state; + sv = list_last_entry(&state->variables, struct state_variable, list); + backend_raw->size_data = sv->start + sv->size; backend_raw->offset = offset; - backend_raw->size_full = backend_raw->size_data + sizeof(struct backend_raw_header); + backend_raw->size = size; + backend_raw->size_full = backend_raw->size_data + + sizeof(struct backend_raw_header); state->backend = backend; - ret = get_meminfo(backend->path, &meminfo); - if (!ret) { - if (!size) - size = meminfo.size; - if (!(meminfo.flags & MTD_NO_ERASE)) - backend_raw->need_erase = 1; - backend_raw->step = ALIGN(backend_raw->size_full, meminfo.erasesize); - if (verbose) - fprintf(stderr, "%s is a mtd of size %d, adjust stepsize to %ld\n", - path, meminfo.size, backend_raw->step); + ret = mtd_get_meminfo(backend->path, &meminfo); + if (!ret && !(meminfo.flags & MTD_NO_ERASE)) { + backend_raw->need_erase = true; + backend_raw->step = ALIGN(backend_raw->size_full, + meminfo.erasesize); + dev_dbg(&state->dev, "is a mtd, adjust stepsize to %ld\n", + backend_raw->step); } else { backend_raw->step = backend_raw->size_full; } - backend_raw->size = size; - - if (backend_raw->size / backend_raw->step < 2) { - pr_err("not enough space for two copies, have %d, need %d\n", - backend_raw->size, backend_raw->step * 2); + if (backend_raw->size / backend_raw->step < RAW_BACKEND_COPIES) { + dev_err(&state->dev, "not enough space for two copies\n"); ret = -ENOSPC; goto err; } @@ -1091,6 +1235,178 @@ err: return ret; } +/* ------------------------------------------------------------ */ + +#undef asprintf + +static struct variable_type *state_find_type(enum state_variable_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(types); i++) { + if (type == types[i].type) { + return &types[i]; + } + } + + return NULL; +} + +static int state_uint32_set(struct state_variable *var, const char *val) +{ + struct state_uint32 *su32 = to_state_uint32(var); + + su32->value = strtoul(val, NULL, 0); + + return 0; +} + +static char *state_uint32_get(struct state_variable *var) +{ + struct state_uint32 *su32 = to_state_uint32(var); + char *str; + int ret; + + ret = asprintf(&str, "%u", su32->value); + if (ret < 0) + return ERR_PTR(-ENOMEM); + + return str; +} + + +static int state_enum32_set(struct state_variable *sv, const char *val) +{ + struct state_enum32 *enum32 = to_state_enum32(sv); + int i; + + for (i = 0; i < enum32->num_names; i++) { + if (!strcmp(enum32->names[i], val)) { + enum32->value = i; + return 0; + } + } + + return -EINVAL; +} + +static char *state_enum32_get(struct state_variable *var) +{ + struct state_enum32 *enum32 = to_state_enum32(var); + char *str; + int ret; + + ret = asprintf(&str, "%s", enum32->names[enum32->value]); + if (ret < 0) + return ERR_PTR(-ENOMEM); + + return str; +} + +static void state_enum32_info(struct state_variable *var) +{ + struct state_enum32 *enum32 = to_state_enum32(var); + int i; + + printf(", values=["); + + for (i = 0; i < enum32->num_names; i++) + printf("%s%s", enum32->names[i], + i == enum32->num_names - 1 ? "" : ","); + printf("]"); +} + +static int string_to_ethaddr(const char *str, uint8_t enetaddr[6]) +{ + int reg; + char *e; + + if (!str || strlen(str) != 17) { + memset(enetaddr, 0, 6); + return -EINVAL; + } + + if (str[2] != ':' || str[5] != ':' || str[8] != ':' || + str[11] != ':' || str[14] != ':') + return -EINVAL; + + for (reg = 0; reg < 6; ++reg) { + enetaddr[reg] = strtoul(str, &e, 16); + str = e + 1; + } + + return 0; +} + +static int state_mac_set(struct state_variable *var, const char *val) +{ + struct state_mac *mac = to_state_mac(var); + uint8_t mac_save[6]; + int ret; + + ret = string_to_ethaddr(val, mac_save); + if (ret) + return ret; + + memcpy(mac->value, mac_save, ARRAY_SIZE(mac_save)); + + return 0; +} + +static char *state_mac_get(struct state_variable *var) +{ + struct state_mac *mac = to_state_mac(var); + char *str; + int ret; + + ret = asprintf(&str, "%02x:%02x:%02x:%02x:%02x:%02x", + mac->value[0], mac->value[1], mac->value[2], + mac->value[3], mac->value[4], mac->value[5]); + if (ret < 0) + return ERR_PTR(-ENOMEM); + + return str; +} + +static char *state_get_var(struct state *state, const char *var) +{ + struct state_variable *sv; + struct variable_type *vtype; + + sv = state_find_var(state, var); + if (IS_ERR(sv)) + return NULL; + + vtype = state_find_type(sv->type); + + return vtype->get(sv); +} + +static int state_set_var(struct state *state, const char *var, const char *val) +{ + struct state_variable *sv; + struct variable_type *vtype; + int ret; + + sv = state_find_var(state, var); + if (IS_ERR(sv)) + return PTR_ERR(sv); + + vtype = state_find_type(sv->type); + + if (!vtype->set) + return -EPERM; + + ret = vtype->set(sv, val); + if (ret) + return ret; + + state->dirty = 1; + + return 0; +} + + static struct state *state_get(const char *name) { struct device_node *root, *node, *partition_node; |