diff options
Diffstat (limited to 'common/eeprom')
-rw-r--r-- | common/eeprom/eeprom_field.c | 250 | ||||
-rw-r--r-- | common/eeprom/eeprom_layout.c | 125 |
2 files changed, 375 insertions, 0 deletions
diff --git a/common/eeprom/eeprom_field.c b/common/eeprom/eeprom_field.c new file mode 100644 index 0000000..7f095a6 --- /dev/null +++ b/common/eeprom/eeprom_field.c @@ -0,0 +1,250 @@ +/* + * (C) Copyright 2009-2016 CompuLab, Ltd. + * + * Authors: Nikita Kiryanov <nikita@compulab.co.il> + * Igor Grinberg <grinberg@compulab.co.il> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/string.h> +#include <eeprom_field.h> + +static void __eeprom_field_print_bin(const struct eeprom_field *field, + char *delimiter, bool reverse) +{ + int i; + int from = reverse ? field->size - 1 : 0; + int to = reverse ? 0 : field->size - 1; + + printf(PRINT_FIELD_SEGMENT, field->name); + for (i = from; i != to; reverse ? i-- : i++) + printf("%02x%s", field->buf[i], delimiter); + + printf("%02x\n", field->buf[i]); +} + +static int __eeprom_field_update_bin(struct eeprom_field *field, + const char *value, bool reverse) +{ + int len = strlen(value); + int k, j, i = reverse ? len - 1 : 0; + unsigned char byte; + char *endptr; + + /* each two characters in the string fit in one byte */ + if (len > field->size * 2) + return -1; + + memset(field->buf, 0, field->size); + + /* i - string iterator, j - buf iterator */ + for (j = 0; j < field->size; j++) { + byte = 0; + char tmp[3] = { 0, 0, 0 }; + + if ((reverse && i < 0) || (!reverse && i >= len)) + break; + + for (k = 0; k < 2; k++) { + if (reverse && i == 0) { + tmp[k] = value[i]; + break; + } + + tmp[k] = value[reverse ? i - 1 + k : i + k]; + } + + byte = simple_strtoul(tmp, &endptr, 0); + if (*endptr != '\0' || byte < 0) + return -1; + + field->buf[j] = byte; + i = reverse ? i - 2 : i + 2; + } + + return 0; +} + +static int __eeprom_field_update_bin_delim(struct eeprom_field *field, + char *value, char *delimiter) +{ + int count = 0; + int i, val; + const char *tmp = value; + char *tok; + char *endptr; + + tmp = strstr(tmp, delimiter); + while (tmp != NULL) { + count++; + tmp++; + tmp = strstr(tmp, delimiter); + } + + if (count > field->size) + return -1; + + tok = strtok(value, delimiter); + for (i = 0; tok && i < field->size; i++) { + val = simple_strtoul(tok, &endptr, 0); + if (*endptr != '\0') + return -1; + + /* here we assume that each tok is no more than byte long */ + field->buf[i] = (unsigned char)val; + tok = strtok(NULL, delimiter); + } + + return 0; +} + +/** + * eeprom_field_print_bin() - print a field which contains binary data + * + * Treat the field data as simple binary data, and print it as two digit + * hexadecimal values. + * Sample output: + * Field Name 0102030405060708090a + * + * @field: an initialized field to print + */ +void eeprom_field_print_bin(const struct eeprom_field *field) +{ + __eeprom_field_print_bin(field, "", false); +} + +/** + * eeprom_field_update_bin() - Update field with new data in binary form + * + * @field: an initialized field + * @value: a string of values (i.e. "10b234a") + */ +int eeprom_field_update_bin(struct eeprom_field *field, char *value) +{ + return __eeprom_field_update_bin(field, value, false); +} + +/** + * eeprom_field_update_reserved() - Update reserved field with new data in + * binary form + * + * @field: an initialized field + * @value: a space delimited string of byte values (i.e. "1 02 3 0x4") + */ +int eeprom_field_update_reserved(struct eeprom_field *field, char *value) +{ + return __eeprom_field_update_bin_delim(field, value, " "); +} + +/** + * eeprom_field_print_bin_rev() - print a field which contains binary data in + * reverse order + * + * Treat the field data as simple binary data, and print it in reverse order + * as two digit hexadecimal values. + * + * Data in field: + * 0102030405060708090a + * Sample output: + * Field Name 0a090807060504030201 + * + * @field: an initialized field to print + */ +void eeprom_field_print_bin_rev(const struct eeprom_field *field) +{ + __eeprom_field_print_bin(field, "", true); +} + +/** + * eeprom_field_update_bin_rev() - Update field with new data in binary form, + * storing it in reverse + * + * This function takes a string of byte values, and stores them + * in the field in the reverse order. i.e. if the input string was "1234", + * "3412" will be written to the field. + * + * @field: an initialized field + * @value: a string of byte values + */ +int eeprom_field_update_bin_rev(struct eeprom_field *field, char *value) +{ + return __eeprom_field_update_bin(field, value, true); +} + +/** + * eeprom_field_print_mac_addr() - print a field which contains a mac address + * + * Treat the field data as simple binary data, and print it formatted as a MAC + * address. + * Sample output: + * Field Name 01:02:03:04:05:06 + * + * @field: an initialized field to print + */ +void eeprom_field_print_mac(const struct eeprom_field *field) +{ + __eeprom_field_print_bin(field, ":", false); +} + +/** + * eeprom_field_update_mac() - Update a mac address field which contains binary + * data + * + * @field: an initialized field + * @value: a colon delimited string of byte values (i.e. "1:02:3:ff") + */ +int eeprom_field_update_mac(struct eeprom_field *field, char *value) +{ + return __eeprom_field_update_bin_delim(field, value, ":"); +} + +/** + * eeprom_field_print_ascii() - print a field which contains ASCII data + * @field: an initialized field to print + */ +void eeprom_field_print_ascii(const struct eeprom_field *field) +{ + char format[8]; + + sprintf(format, "%%.%ds\n", field->size); + printf(PRINT_FIELD_SEGMENT, field->name); + printf(format, field->buf); +} + +/** + * eeprom_field_update_ascii() - Update field with new data in ASCII form + * @field: an initialized field + * @value: the new string data + * + * Returns 0 on success, -1 of failure (new string too long). + */ +int eeprom_field_update_ascii(struct eeprom_field *field, char *value) +{ + if (strlen(value) >= field->size) { + printf("%s: new data too long\n", field->name); + return -1; + } + + strncpy((char *)field->buf, value, field->size - 1); + field->buf[field->size - 1] = '\0'; + + return 0; +} + +/** + * eeprom_field_print_reserved() - print the "Reserved fields" field + * + * Print a notice that the following field_size bytes are reserved. + * + * Sample output: + * Reserved fields (64 bytes) + * + * @field: an initialized field to print + */ +void eeprom_field_print_reserved(const struct eeprom_field *field) +{ + printf(PRINT_FIELD_SEGMENT, "Reserved fields\t"); + printf("(%d bytes)\n", field->size); +} diff --git a/common/eeprom/eeprom_layout.c b/common/eeprom/eeprom_layout.c new file mode 100644 index 0000000..c059233 --- /dev/null +++ b/common/eeprom/eeprom_layout.c @@ -0,0 +1,125 @@ +/* + * (C) Copyright 2009-2016 CompuLab, Ltd. + * + * Authors: Nikita Kiryanov <nikita@compulab.co.il> + * Igor Grinberg <grinberg@compulab.co.il> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/kernel.h> +#include <eeprom_layout.h> +#include <eeprom_field.h> + +#define NO_LAYOUT_FIELDS "Unknown layout. Dumping raw data\n" + +struct eeprom_field layout_unknown[1] = { + { NO_LAYOUT_FIELDS, 256, NULL, eeprom_field_print_bin, + eeprom_field_update_bin }, +}; + +/* + * eeprom_layout_detect() - detect layout based on the contents of the data. + * @data: Pointer to the data to be analyzed. + * + * Returns: the detected layout version. + */ +__weak int eeprom_layout_detect(unsigned char *data) +{ + return LAYOUT_VERSION_UNRECOGNIZED; +} + +/* + * __eeprom_layout_assign() - set the layout fields + * @layout: A pointer to an existing struct layout. + * @layout_version: The version number of the desired layout + */ +__weak void __eeprom_layout_assign(struct eeprom_layout *layout, + int layout_version) +{ + layout->fields = layout_unknown; + layout->num_of_fields = ARRAY_SIZE(layout_unknown); +} +void eeprom_layout_assign(struct eeprom_layout *layout, int layout_version) \ + __attribute__((weak, alias("__eeprom_layout_assign"))); + +/* + * eeprom_layout_print() - print the layout and the data which is assigned to it + * @layout: A pointer to an existing struct layout. + */ +static void eeprom_layout_print(const struct eeprom_layout *layout) +{ + int i; + struct eeprom_field *fields = layout->fields; + + for (i = 0; i < layout->num_of_fields; i++) + fields[i].print(&fields[i]); +} + +/* + * eeprom_layout_update_field() - update a single field in the layout data. + * @layout: A pointer to an existing struct layout. + * @field_name: The name of the field to update. + * @new_data: The new field data (a string. Format depends on the field) + * + * Returns: 0 on success, negative error value on failure. + */ +static int eeprom_layout_update_field(struct eeprom_layout *layout, + char *field_name, char *new_data) +{ + int i, err; + struct eeprom_field *fields = layout->fields; + + if (new_data == NULL) + return 0; + + if (field_name == NULL) + return -1; + + for (i = 0; i < layout->num_of_fields; i++) { + if (fields[i].name == RESERVED_FIELDS || + strcmp(fields[i].name, field_name)) + continue; + + err = fields[i].update(&fields[i], new_data); + if (err) + printf("Invalid data for field %s\n", field_name); + + return err; + } + + printf("No such field '%s'\n", field_name); + + return -1; +} + +/* + * eeprom_layout_setup() - setup layout struct with the layout data and + * metadata as dictated by layout_version + * @layout: A pointer to an existing struct layout. + * @buf: A buffer initialized with the eeprom data. + * @buf_size: Size of buf in bytes. + * @layout version: The version number of the layout. + */ +void eeprom_layout_setup(struct eeprom_layout *layout, unsigned char *buf, + unsigned int buf_size, int layout_version) +{ + int i; + + if (layout_version == LAYOUT_VERSION_AUTODETECT) + layout->layout_version = eeprom_layout_detect(buf); + else + layout->layout_version = layout_version; + + eeprom_layout_assign(layout, layout_version); + layout->data = buf; + for (i = 0; i < layout->num_of_fields; i++) { + layout->fields[i].buf = buf; + buf += layout->fields[i].size; + } + + layout->data_size = buf_size; + layout->print = eeprom_layout_print; + layout->update = eeprom_layout_update_field; +} |