├── .gitignore ├── setversion ├── api.h ├── command.h ├── field.h ├── layout.h ├── common.h ├── common.c ├── Makefile ├── README.adoc ├── command.c ├── CHANGELOG.adoc ├── linux_api.c ├── layout.c ├── field.c ├── LICENSE └── parser.c /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE! Don't add files that are generated in specific 3 | # subdirectories here. Add them in the ".gitignore" file 4 | # in that subdirectory instead. 5 | # 6 | # Normal rules 7 | # 8 | 9 | *.rej 10 | *.orig 11 | *.a 12 | *.o 13 | *.su 14 | *~ 15 | *.swp 16 | *.patch 17 | *.bin 18 | 19 | # 20 | # Additional rules 21 | # 22 | eeprom-util 23 | auto_generated.h 24 | /dep 25 | /obj -------------------------------------------------------------------------------- /setversion: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Print additional version information for non-release trees. 3 | 4 | usage() { 5 | echo "Usage: $0 [srctree]" >&2 6 | exit 1 7 | } 8 | 9 | cd "${1:-.}" || usage 10 | 11 | # Check for git and a git repo. 12 | if head=`git rev-parse --verify HEAD 2>/dev/null`; then 13 | # Do we have an untagged version? 14 | if git name-rev --tags HEAD | \ 15 | grep -E '^HEAD[[:space:]]+(.*~[0-9]*|undefined)$' > /dev/null; then 16 | git describe --tags | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}' 17 | fi 18 | 19 | # Are there uncommitted changes? 20 | git update-index --refresh --unmerged > /dev/null 21 | if git diff-index --name-only HEAD | read dummy; then 22 | printf '%s' -dirty 23 | fi 24 | 25 | # Is this git on svn? 26 | if git config --get svn-remote.svn.url >/dev/null; then 27 | printf -- '-svn%s' "`git-svn find-rev $head`" 28 | fi 29 | fi 30 | 31 | # Check for svn and a svn repo. 32 | if rev=`svn info 2>/dev/null` ; then 33 | rev=`echo "${rev}" | grep '^Revision' | awk '{print $NF}'` 34 | printf -- '-svn%s' $rev 35 | fi 36 | 37 | # Check for any localversion-* files 38 | printf '%s' "`cat localversion-* 2>/dev/null`" 39 | -------------------------------------------------------------------------------- /api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2011 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * Igor Grinberg 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef API_H_ 21 | #define API_H_ 22 | 23 | struct api { 24 | int fd; 25 | int i2c_bus; 26 | int i2c_addr; 27 | 28 | int (*read)(struct api *api, unsigned char *buf, int offset, int size); 29 | int (*write)(struct api *api, unsigned char *buf, int offset, int size); 30 | int (*probe)(struct api *api); 31 | void (*system_error)(const char *message); 32 | }; 33 | 34 | void api_init(struct api *api, int i2c_bus, int i2c_addr); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /command.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2011 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * Igor Grinberg 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef _COMMAND_ 21 | #define _COMMAND_ 22 | 23 | #include "common.h" 24 | #include "layout.h" 25 | 26 | enum action { 27 | EEPROM_READ, 28 | EEPROM_WRITE_FIELDS, 29 | EEPROM_WRITE_BYTES, 30 | EEPROM_LIST, 31 | EEPROM_CLEAR, 32 | EEPROM_CLEAR_FIELDS, 33 | EEPROM_CLEAR_BYTES, 34 | EEPROM_ACTION_INVALID, 35 | }; 36 | 37 | struct options { 38 | int i2c_bus; 39 | int i2c_addr; 40 | enum layout_version layout_ver; 41 | enum print_format print_format; 42 | }; 43 | 44 | struct command { 45 | enum action action; 46 | struct options *opts; 47 | struct data_array *data; 48 | 49 | int (*execute)(struct command *cmd); 50 | }; 51 | 52 | struct command *new_command(enum action action, struct options *options, 53 | struct data_array *data); 54 | void free_command(struct command *cmd); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /field.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2011 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * Igor Grinberg 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef _FIELD_ 21 | #define _FIELD_ 22 | 23 | #include 24 | 25 | enum field_type { 26 | FIELD_BINARY, 27 | FIELD_REVERSED, 28 | FIELD_VERSION, 29 | FIELD_ASCII, 30 | FIELD_MAC, 31 | FIELD_DATE, 32 | FIELD_RESERVED, 33 | FIELD_RAW, 34 | }; 35 | 36 | enum print_format { 37 | FORMAT_DEFAULT, 38 | FORMAT_DUMP, 39 | }; 40 | 41 | struct field { 42 | char *name; 43 | char *short_name; 44 | int data_size; 45 | enum field_type type; 46 | unsigned char *data; 47 | struct field_ops *ops; 48 | }; 49 | 50 | struct field_ops { 51 | int (*get_data_size)(const struct field *field); 52 | bool (*is_named)(const struct field *field, const char *str); 53 | void (*print_value)(const struct field *field); 54 | void (*print)(const struct field *field); 55 | int (*update)(struct field *field, char *value); 56 | void (*clear)(struct field *field); 57 | }; 58 | 59 | void init_field(struct field *field, unsigned char *data, 60 | enum print_format print_format); 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /layout.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2017 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * Igor Grinberg 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef _LAYOUT_ 21 | #define _LAYOUT_ 22 | 23 | #include "common.h" 24 | #include "field.h" 25 | 26 | #define EEPROM_SIZE 256 27 | 28 | enum layout_version { 29 | LAYOUT_AUTODETECT = -1, 30 | LAYOUT_LEGACY, 31 | LAYOUT_VER1, 32 | LAYOUT_VER2, 33 | LAYOUT_VER3, 34 | LAYOUT_VER4, 35 | LAYOUT_UNRECOGNIZED, /* marks the end of the layout versions */ 36 | RAW_DATA, 37 | }; 38 | 39 | struct layout { 40 | struct field *fields; 41 | int num_of_fields; 42 | enum layout_version layout_version; 43 | unsigned char *data; 44 | int data_size; 45 | void (*print)(const struct layout *layout); 46 | int (*update_fields)(struct layout *layout, 47 | struct data_array *data); 48 | int (*clear_fields)(struct layout *layout, 49 | struct data_array *data); 50 | int (*update_bytes)(struct layout *layout, 51 | struct data_array *data); 52 | int (*clear_bytes)(struct layout *layout, 53 | struct data_array *data); 54 | }; 55 | 56 | struct layout *new_layout(unsigned char *buf, unsigned int buf_size, 57 | enum layout_version layout_version, 58 | enum print_format print_format); 59 | void free_layout(struct layout *layout); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2011 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * Igor Grinberg 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef _COMMON_ 21 | #define _COMMON_ 22 | 23 | #define COLOR_RED "\x1B[31m" 24 | #define COLOR_GREEN "\x1B[32m" 25 | #define COLOR_RESET "\033[0m" 26 | 27 | #define MIN_I2C_BUS 0 28 | #define MAX_I2C_BUS 255 29 | 30 | #define MIN_I2C_ADDR 0x03 31 | #define MAX_I2C_ADDR 0x77 32 | 33 | #define STR_ENO_MEM "Out of memory" 34 | 35 | // Macro for printing error messages 36 | #define eprintf(args...) fprintf (stderr, args) 37 | // Macro for printing input error messages 38 | #define ie_fmt(fmt) "Input error: " fmt " - Operation Aborted!\n" 39 | #define ieprintf(fmt, ...) eprintf(ie_fmt(fmt), ##__VA_ARGS__) 40 | 41 | // Macro for handling debug checks 42 | #ifndef DEBUG 43 | #define ASSERT(args) ((void)0) 44 | #else // ifndef DEBUG 45 | void failed_assert(const char* func, char *file, int line); 46 | #define ASSERT(arg) ((arg) ? ((void)0) : \ 47 | failed_assert(__func__, __FILE__, __LINE__)) 48 | #endif // ifndef DEBUG 49 | 50 | struct field_change { 51 | char *field; 52 | char *value; 53 | }; 54 | 55 | struct bytes_change { 56 | int start; 57 | int end; 58 | int value; 59 | }; 60 | 61 | struct bytes_range { 62 | int start; 63 | int end; 64 | }; 65 | 66 | struct data_array { 67 | int size; 68 | union { 69 | struct field_change *fields_changes; 70 | struct bytes_change *bytes_changes; 71 | char **fields_list; 72 | struct bytes_range *bytes_list; 73 | }; 74 | }; 75 | 76 | #define STRTOI_STR_CON 1 77 | #define STRTOI_STR_END 2 78 | int strtoi_base(char **str, int *dest, int base); 79 | int strtoi(char **str, int *dest); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /common.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2011 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "common.h" 23 | 24 | #ifdef DEBUG 25 | void failed_assert(const char* func, char *file, int line) 26 | { 27 | eprintf("Assertion Failed in %s() (%s:%d)\n", func, file, line); 28 | exit(1); 29 | } 30 | #endif /* ifdef DEBUG */ 31 | 32 | /* 33 | * strtoi_base - convert to int using the given numerical base and point 34 | * to the first character after the number 35 | * 36 | * @str: A pointer to a string containing an integer number at the 37 | * beginning. On success the pointer will point to the first 38 | * character after the number. 39 | * @dest: A pointer where to save the int result 40 | * @base: The numerical base of the characters in the input string. 41 | * If 0 the base is determined by the format. 42 | * 43 | * Returns: STRTOI_STR_END on success and all characters read. 44 | * STRTOI_STR_CON on success and additional characters remain. 45 | * -ERANGE or -EINVAL on failure 46 | */ 47 | int strtoi_base(char **str, int *dest, int base) 48 | { 49 | ASSERT(str && *str && dest); 50 | 51 | if (**str == '\0') 52 | return -EINVAL; 53 | 54 | char *endptr; 55 | errno = 0; 56 | int num = strtol(*str, &endptr, base); 57 | 58 | if (errno != 0) 59 | return -errno; 60 | 61 | if (*str == endptr) 62 | return -EINVAL; 63 | 64 | *dest = num; 65 | *str = endptr; 66 | 67 | if (*endptr == 0) 68 | return STRTOI_STR_END; 69 | 70 | return STRTOI_STR_CON; 71 | } 72 | 73 | /* 74 | * strtoi - convert to int and point to the first character after the number 75 | * 76 | * @str: A pointer to a string containing an integer number at the 77 | * beginning. On success the pointer will point to the first 78 | * character after the number. 79 | * @dest: A pointer where to save the int result 80 | * 81 | * Returns: STRTOI_STR_END on success and all characters read. 82 | * STRTOI_STR_CON on success and additional characters remain. 83 | * -ERANGE or -EINVAL on failure 84 | */ 85 | int strtoi(char **str, int *dest) 86 | { 87 | return strtoi_base(str, dest, 0); 88 | } 89 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2012 CompuLab, Ltd. 3 | # Authors: Nikita Kiryanov 4 | # Igor Grinberg 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | VERSION = 3 21 | MINORVER = 2 22 | PATCHLEVEL = 0 23 | EEPROM_UTIL_VERSION = $(VERSION).$(MINORVER).$(PATCHLEVEL) 24 | 25 | CROSS_COMPILE ?= 26 | CC = $(CROSS_COMPILE)gcc 27 | 28 | OBJDIR := obj 29 | DEPDIR := dep 30 | 31 | TARGET := eeprom-util 32 | GOAL_FILE := $(OBJDIR)/make_goal 33 | AUTO_GENERATED_FILE := auto_generated.h 34 | 35 | CORE := common.o field.o layout.o command.o linux_api.o 36 | MAIN := parser.o 37 | 38 | OBJECTS := $(addprefix $(OBJDIR)/,$(CORE)) 39 | DEPS := $(addprefix $(DEPDIR)/,$(CORE:.o=.d) $(MAIN:.o=.d)) 40 | 41 | CFLAGS = -Wall -std=gnu99 42 | DEPFLAGS = -MMD -MF $(DEPDIR)/$(*F).d 43 | WRITEFLAGS = -D ENABLE_WRITE 44 | DEBUGFLAGS = -g -D DEBUG 45 | 46 | $(TARGET): $(OBJECTS) $(AUTO_GENERATED_FILE) $(OBJDIR)/$(MAIN) 47 | $(CC) $(LDFLAGS) $(OBJECTS) $(OBJDIR)/$(MAIN) -o $(TARGET) 48 | 49 | $(OBJDIR)/%.o : %.c $(GOAL_FILE) 50 | $(CC) $(CFLAGS) $(DEPFLAGS) -c -o $@ $< 51 | 52 | # pull in dependency info for *existing* .o files 53 | -include $(DEPS) 54 | 55 | static: LDFLAGS += -static 56 | static: $(TARGET) ; 57 | 58 | write: CFLAGS += $(WRITEFLAGS) 59 | write: $(TARGET) ; 60 | 61 | write_static: LDFLAGS += -static 62 | write_static: write ; 63 | 64 | debug: CFLAGS += $(DEBUGFLAGS) 65 | debug: write ; 66 | 67 | $(AUTO_GENERATED_FILE): $(OBJECTS) $(MAIN:.o=.c) 68 | @( printf '#define VERSION "%s%s"\n' "$(EEPROM_UTIL_VERSION)" \ 69 | '$(shell ./setversion)' ) > $@ 70 | @date +'#define BUILD_DATE "%d %b %C%y"' >> $@ 71 | @date +'#define BUILD_TIME "%T"' >> $@ 72 | 73 | # make directory only if they don't exists (prevent unnecessary recompiling) 74 | $(OBJECTS): | $(OBJDIR) 75 | $(DEPS): | $(DEPDIR) 76 | $(OBJDIR): 77 | @mkdir -p $(OBJDIR) 78 | $(DEPDIR): 79 | @mkdir -p $(DEPDIR) 80 | 81 | # save goal to file so target will recompile if goal changes 82 | $(GOAL_FILE): .FORCE | $(OBJDIR) 83 | ifneq ($(CROSS_COMPILE)$(MAKECMDGOALS),$(shell cat $(GOAL_FILE) 2>&1)) 84 | @echo '$(CROSS_COMPILE)$(MAKECMDGOALS)' > $@ 85 | endif 86 | 87 | # fix implicit pattern rules to compile without liniking 88 | $(CORE:.o=): % : $(OBJDIR)/%.o ; 89 | $(MAIN:.o=): % : $(AUTO_GENERATED_FILE) $(OBJDIR)/%.o ; 90 | 91 | clean: 92 | rm -rf $(TARGET) $(OBJDIR) $(DEPDIR) $(AUTO_GENERATED_FILE) 93 | 94 | .PHONY: clean .FORCE 95 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = CompuLab EEPROM utility 2 | 3 | This utility displays and updates EEPROM information. It works for any EEPROM, 4 | but has special support for Compulab SoMs by being aware of the EEPROM 5 | layout, and using that information to display the EEPROM contents in a human 6 | readable format. 7 | 8 | The utility provides a WYSIWYG UI for updating EEPROM contents; the user can 9 | specify the target field by name, and provide the new value in the same 10 | formatting as displayed by the `read` command. The user can also clear a field 11 | by name. Direct offset/value mode of writing is also supported. 12 | 13 | Finally, the utility can list accessible devices on i2c buses, or a more 14 | concise listing of just the EEPROM devices, depending on the available 15 | interfaces in the OS. 16 | 17 | Example run: 18 | ---- 19 | # ./eeprom-util read 2 0x50 20 | Major Revision 1.00 21 | Minor Revision 0.00 22 | 1st MAC Address 00:01:c0:13:91:d0 23 | 2nd MAC Address 00:01:ca:12:91:d0 24 | Production Date 29/Aug/2013 25 | Serial Number ffffffffffff123456789abc 26 | 3rd MAC Address (WIFI) ff:ff:ff:ff:ff:ff 27 | 4th MAC Address (Bluetooth) ff:ff:ff:ff:ff:ff 28 | Layout Version 02 29 | Reserved fields (83 bytes) 30 | Product Name CM-FX6 31 | Product Options #1 C1200QM-D2G-N0- 32 | Product Options #2 ND0-U5-E-A-WB 33 | Product Options #3 34 | Reserved fields (64 bytes) 35 | 36 | # ./eeprom-util write fields 2 0x50 "Production Date=01/Jun/2014" 37 | # ./eeprom-util clear fields 2 0x50 "Serial Number" "Minor Revision" 38 | ---- 39 | 40 | = Requirements 41 | 42 | The utility requires a Linux system with either /dev/i2c interface, or a loaded 43 | EEPROM driver (at24). 44 | 45 | = Building 46 | 47 | To build the utility you can type one of the following: 48 | 49 | * `make` - Only read functionality will be supported, system default linking type. 50 | * `make write` - Enable the write capabilities, system default linking type. 51 | * `make static` - Only read functionality, force static linking of libraries. 52 | * `make write_static` - Enable the write capabilities, force static linking of libraries. 53 | * `make debug` - Enable the write capabilities, compile with debug flags. 54 | 55 | = Adding custom EEPROM layouts 56 | 57 | To add support for your custom EEPROM layouts do the following: 58 | 59 | *layout.c*: 60 | 61 | * Create a new `struct field` array with the fields of your layout. 62 | + 63 | Each field must define a name, a short name, a size (in bytes) and a type. 64 | + 65 | The available field's types are listed under `enum field_type` in `field.h`. 66 | + 67 | The total size of all fields must not exceed the defined `EEPROM_SIZE` in 68 | `layout.h` 69 | * Update `detect_layout()` function to properly auto-detect your layout. 70 | + 71 | Notice the value of `LAYOUT_CHECK_BYTE`. It is the offset of the "layout 72 | version" field. 73 | * Update `build_layout()` function to select the fields array of your layout. 74 | 75 | *layout.h*: 76 | 77 | * Add the version or the name of your layout to `enum layout_version` 78 | 79 | *parser.c*: 80 | 81 | * (optional) Update `parse_layout_version()` to enable manual selection of your 82 | layout using the `-l ` command line option. 83 | 84 | WARNING: Some features of the utility may not work correctly if your custom layout 85 | does not retain backward compatibility with existing Compulab layouts. To 86 | ensure full support, your custom layout should expand existing layouts only 87 | within the reserved fields area. In any case, try not to change the offset of 88 | the "Layout Version" field to avoid complicating the auto detection of your 89 | layout by the utility. 90 | -------------------------------------------------------------------------------- /command.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2011 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * Igor Grinberg 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "command.h" 26 | #include "layout.h" 27 | #include "api.h" 28 | 29 | static struct api api; 30 | static unsigned char buf[EEPROM_SIZE]; 31 | 32 | static int read_eeprom(unsigned char *buf) 33 | { 34 | int ret = api.read(&api, buf, 0, EEPROM_SIZE); 35 | 36 | if (ret < 0) 37 | api.system_error("Read error"); 38 | 39 | return ret; 40 | } 41 | 42 | static int write_eeprom(unsigned char *data) 43 | { 44 | int ret = api.write(&api, data, 0, EEPROM_SIZE); 45 | 46 | if (ret < 0) 47 | api.system_error("Write error"); 48 | 49 | return ret; 50 | } 51 | 52 | static struct layout *prepare_layout(struct command *cmd) 53 | { 54 | if (read_eeprom(buf) < 0) 55 | return NULL; 56 | 57 | struct layout *layout = NULL; 58 | layout = new_layout(buf, EEPROM_SIZE, cmd->opts->layout_ver, 59 | cmd->opts->print_format); 60 | 61 | if (!layout) 62 | api.system_error("Memory allocation error"); 63 | 64 | return layout; 65 | } 66 | 67 | static int execute_command(struct command *cmd) 68 | { 69 | ASSERT(cmd && cmd->action != EEPROM_ACTION_INVALID); 70 | 71 | int ret = -1; 72 | struct layout *layout = NULL; 73 | 74 | api_init(&api, cmd->opts->i2c_bus, cmd->opts->i2c_addr); 75 | 76 | if (cmd->action == EEPROM_LIST) 77 | return api.probe(&api); 78 | 79 | if (cmd->action == EEPROM_CLEAR) { 80 | memset(buf, 0xff, EEPROM_SIZE); 81 | return write_eeprom(buf); 82 | } 83 | 84 | layout = prepare_layout(cmd); 85 | if (!layout) 86 | return -1; 87 | 88 | switch(cmd->action) { 89 | case EEPROM_READ: 90 | layout->print(layout); 91 | ret = 0; 92 | goto done; 93 | case EEPROM_WRITE_FIELDS: 94 | if (!layout->update_fields(layout, cmd->data)) { 95 | ret = -1; 96 | goto done; 97 | } 98 | break; 99 | case EEPROM_WRITE_BYTES: 100 | if (!layout->update_bytes(layout, cmd->data)) { 101 | ret = -1; 102 | goto done; 103 | } 104 | break; 105 | case EEPROM_CLEAR_FIELDS: 106 | if (!layout->clear_fields(layout, cmd->data)) { 107 | ret = -1; 108 | goto done; 109 | } 110 | break; 111 | case EEPROM_CLEAR_BYTES: 112 | if (!layout->clear_bytes(layout, cmd->data)) { 113 | ret = -1; 114 | goto done; 115 | } 116 | break; 117 | default: 118 | goto done; 119 | } 120 | 121 | ret = write_eeprom(layout->data); 122 | 123 | done: 124 | free_layout(layout); 125 | return ret; 126 | } 127 | 128 | struct command *new_command(enum action action, struct options *options, 129 | struct data_array *data) 130 | { 131 | struct command *cmd = malloc(sizeof(struct command)); 132 | if (!cmd) 133 | return cmd; 134 | 135 | cmd->action = action; 136 | cmd->opts = options; 137 | cmd->data = data; 138 | cmd->execute = execute_command; 139 | 140 | return cmd; 141 | } 142 | 143 | void free_command(struct command *cmd) 144 | { 145 | free(cmd); 146 | } 147 | -------------------------------------------------------------------------------- /CHANGELOG.adoc: -------------------------------------------------------------------------------- 1 | = Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on http://keepachangelog.com/[Keep a Changelog] 6 | and this project adheres to http://semver.org/[Semantic Versioning] 7 | since version v3.0.0. 8 | 9 | == <> - 2018-06-13 10 | === Added 11 | * Add a "dump" print format for the `read` command. The output of this format is 12 | usable for later input using the `write fields` command. 13 | * Extend the `write bytes` command to accept a sequence of bytes with different 14 | values. The input syntax is: `,,,...`. 15 | * Add the ability to accept input from both the command line and the standard 16 | input simultaneously. 17 | * Support comments in input files using the ';' symbol. 18 | * Add a "scratchpad" binary field on layout version 4. 19 | 20 | === Changed 21 | * Change the accepted address range for I2C device to 0x03 - 0x77. 22 | * Change the `write fields` command to clear fields with empty values, instead 23 | of rejecting an input that contains fields with empty values. 24 | * Refine the errors about invalid command line arguments. 25 | 26 | === Fixed 27 | * Fix a segmentation fault when calling the `write fields` or `clear fields` 28 | commands when the layout is unknown. 29 | * Fix problems with `list` command not reporting some access errors or printing 30 | wrong output on some bus numbers. 31 | 32 | == <> - 2018-04-18 33 | === Added 34 | * `clear` command can also clear by `fields` or by `bytes`. 35 | * Each field on the current layouts now has an additional short name. For the 36 | `write` or `clear` commmands, the user can now specify a field by its short 37 | name or its full name. 38 | 39 | === Fixed 40 | * Simultaneous access to the same device could resulted in a faulty read. 41 | * Syntax enforcement of input values for fields wasn't very strict, and it 42 | caused some unexpected behaviors. 43 | 44 | == <> - 2018-03-20 45 | 46 | === Fixed 47 | * Sometimes, an empty ASCII field was displayed as a series of unknown 48 | characters instead of being displayed as blank. 49 | 50 | == <> - 2017-09-19 51 | 52 | === Changed 53 | * Utility now returns proper return values, instead of mostly returning 0. This 54 | makes it suitable for scripting, since the script can now tell if something 55 | goes wrong. 56 | * If an EEPROM field that contains string data has no valid data, a field name 57 | with no data is printed. 58 | * The utility no longer allows partial updates of the EEPROM. If the `write` 59 | command contains invalid fields, offsets, or bad syntax, no changes will be 60 | made to the EEPROM contents. Previously, the utility would update what it 61 | can, and display a warning for what it couldn't update. 62 | * i2c bus and address values are now limited to 0-256. Any value outside of 63 | these boundries will cause an error. 64 | * Errors are now only printed to stderr. 65 | * User now notified with a helpful message if there are no usable interfaces 66 | for the utility to work with. 67 | 68 | === Added 69 | * Support for EEPROM layout version 4. 70 | * New special layout arguments: 71 | ** `raw` layout which produces a formatted hex dump. 72 | ** `auto` layout. It has the same effect as not specifying any layout 73 | value, and is included to improve scriptability. 74 | * `clear` command for clearing all EEPROM contents. 75 | * `write bytes` can now be told to fill a range of addresses with a specific 76 | value. 77 | * `list` command now also works with the EEPROM driver. It produces a listing 78 | of i2c buses and addresses of only EEPROM devices. 79 | 80 | === Deprecated 81 | * Layout versions should now be given as simple numbers. The old v 82 | (v1, v2, etc.) syntax still works for now. "legacy" layout name is still 83 | "legacy". 84 | 85 | == <> - 2015-04-16 86 | === Changed 87 | * Serial number display/update format (no spaces between bytes) 88 | 89 | === Fixed 90 | * `write` command caused serial number to be stored with incorrect byte order. 91 | 92 | == <> - 2014-11-03 93 | === Changed 94 | * This is a major refactor/redesign of the application. One of the goals for 95 | the new design was to make it easy for users to port the utility to a 96 | non-unix-like system. 97 | * New user interface. Changes include: 98 | ** User no longer need to choose between i2c and driver I/O. The utility will 99 | try i2c first, and fallback to driver if needed. 100 | ** Now all invocations require an i2c bus number and device address, and the 101 | syntax for specifying this information has changed. 102 | ** Better format for by-byte changes (offset,byte tuples now separated from 103 | one another) 104 | ** WYSIWYG field updates. The data format that is displayed by the `read` 105 | command is also the format for writing new data to the field.For example, 106 | the following are valid field update arguments: 107 | 108 | MAC address field=01:02:03:04:05:06 109 | Date field=01/Jan/2010 110 | 111 | * Utility banner is now only printed by help and version commands, not on each 112 | invocation of utility. 113 | 114 | === Removed 115 | * Commands no longer echoed back to the user. 116 | * `examples` command. 117 | 118 | === Added 119 | * Support for v3 EEPROM layout. 120 | * Manual layout selection. 121 | * File input for `write` command. 122 | * `list` function can be limited to scanning only one bus. 123 | 124 | == <> - 2013-01-07 125 | === Fixed 126 | * Each I/O operation now properly resets the EEPROM reading pointer. 127 | * ASCII fields are always null terminated. 128 | 129 | == <> - 2012-09-09 130 | === Fixed 131 | * Segfault when displaying date fields with invalid month value. 132 | 133 | == v1.0 - 2012-06-08 134 | === Added 135 | * Display EEPROM contents with layout information and data formatted in human 136 | readable format. 137 | * Update EEPROM contents by specifying offset,byte pairs, or fieldname=data 138 | strings. 139 | * List bus number and address of i2c accessible devices. 140 | * Works with both /dev/i2c interface, and with EEPROM driver. 141 | 142 | [[v3.2.0]] 143 | [v3.2.0]: https://github.com/compulab/eeprom-util/compare/v3.1.0…v3.2.0 144 | 145 | [[v3.1.0]] 146 | [v3.1.0]: https://github.com/compulab/eeprom-util/compare/v3.0.1…v3.1.0 147 | 148 | [[v3.0.1]] 149 | [v3.0.1]: https://github.com/compulab/eeprom-util/compare/v3.0.0…v3.0.1 150 | 151 | [[v3.0.0]] 152 | [v3.0.0]: https://github.com/compulab/eeprom-util/compare/v2.1…v3.0.0 153 | 154 | [[v2.1]] 155 | [v2.1]: https://github.com/compulab/eeprom-util/compare/v2.0…v2.1 156 | 157 | [[v2.0]] 158 | [v2.0]: https://github.com/compulab/eeprom-util/compare/v1.2…v2.0 159 | 160 | [[v1.2]] 161 | [v1.2]: https://github.com/compulab/eeprom-util/compare/v1.1…v1.2 162 | 163 | [[v1.1]] 164 | [v1.1]: https://github.com/compulab/eeprom-util/compare/v1.0…v1.1 -------------------------------------------------------------------------------- /linux_api.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2017 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * Igor Grinberg 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "api.h" 33 | #include "common.h" 34 | 35 | extern int errno; 36 | 37 | /* 38 | * Prepares a device file fd for read or write. 39 | * flags are the standard file open flags. 40 | * On success: returns the fd. 41 | * On failure: -1 42 | */ 43 | static int open_device_file(char *dev_file, int i2c_addr) 44 | { 45 | ASSERT(dev_file); 46 | 47 | int fd = open(dev_file, O_RDWR); 48 | if (fd < 0) 49 | return -1; 50 | 51 | if (i2c_addr >= 0) { 52 | if (ioctl(fd, I2C_SLAVE_FORCE, i2c_addr) < 0) { 53 | close(fd); 54 | return -1; 55 | } 56 | } 57 | 58 | return fd; 59 | } 60 | 61 | static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, 62 | int size, union i2c_smbus_data *data) 63 | { 64 | struct i2c_smbus_ioctl_data args; 65 | 66 | args.read_write = read_write; 67 | args.command = command; 68 | args.size = size; 69 | args.data = data; 70 | 71 | return ioctl(file, I2C_SMBUS, &args); 72 | } 73 | 74 | static int i2c_read(struct api *api, unsigned char *buf, int offset, int size) 75 | { 76 | ASSERT(api && buf); 77 | 78 | int bytes_transferred = 0; 79 | union i2c_smbus_data data; 80 | 81 | for (int i = offset; i < size; i++) { 82 | if (i2c_smbus_access(api->fd, I2C_SMBUS_READ, i, 83 | I2C_SMBUS_BYTE_DATA, &data) < 0) 84 | return -1; 85 | 86 | buf[i] = (unsigned char)(data.byte & 0xFF); 87 | bytes_transferred++; 88 | } 89 | 90 | return bytes_transferred; 91 | } 92 | 93 | /* 94 | * This function supplies the appropriate delay needed for consecutive writes 95 | * via i2c to succeed 96 | */ 97 | static void msleep(unsigned int msecs) 98 | { 99 | struct timespec time = {0, 1000000 * msecs}; 100 | nanosleep(&time, NULL); 101 | } 102 | 103 | static int i2c_write(struct api *api, unsigned char *buf, int offset, int size) 104 | { 105 | ASSERT(api && buf); 106 | 107 | int bytes_transferred = 0; 108 | union i2c_smbus_data data; 109 | 110 | for (int i = offset; i < size; i++) { 111 | data.byte = buf[i]; 112 | if (i2c_smbus_access(api->fd, I2C_SMBUS_WRITE, i, 113 | I2C_SMBUS_BYTE_DATA, &data) < 0) 114 | return -1; 115 | 116 | msleep(5); 117 | bytes_transferred++; 118 | } 119 | 120 | return bytes_transferred; 121 | } 122 | 123 | static int driver_read(struct api *api, unsigned char *buf, int offset, 124 | int size) 125 | { 126 | ASSERT(api && buf); 127 | lseek(api->fd, offset, SEEK_SET); 128 | return read(api->fd, buf + offset, size); 129 | } 130 | 131 | static int driver_write(struct api *api, unsigned char *buf, int offset, 132 | int size) 133 | { 134 | ASSERT(api && buf); 135 | lseek(api->fd, offset, SEEK_SET); 136 | return write(api->fd, buf + offset, size); 137 | } 138 | 139 | static bool i2c_probe(int fd, int addr) 140 | { 141 | union i2c_smbus_data data; 142 | 143 | if (ioctl(fd, I2C_SLAVE_FORCE, addr) < 0) 144 | return false; 145 | 146 | if (i2c_smbus_access(fd, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data) < 0) 147 | return false; 148 | 149 | return true; 150 | } 151 | 152 | #define PRINT_NOT_FOUND(x) eprintf("No "x" was found") 153 | #define PRINT_BUS_NUM(x) (x >= 0) ? eprintf(" on bus %d\n", x) : eprintf("\n") 154 | #define PRINT_DRIVER_HINT(x) eprintf("Is "x" driver loaded?\n") 155 | static int list_i2c_accessible(int bus) 156 | { 157 | ASSERT(bus <= MAX_I2C_BUS); 158 | 159 | int fd, ret = -1; 160 | char dev_file_name[13]; 161 | bool i2c_bus_found = false; 162 | 163 | int i = (bus < 0) ? MIN_I2C_BUS : bus; 164 | int end = (bus < 0) ? MAX_I2C_BUS : bus; 165 | for (; i <= end; i++) { 166 | sprintf(dev_file_name, "/dev/i2c-%d", i); 167 | 168 | fd = open(dev_file_name, O_RDWR); 169 | if (fd < 0 && (errno == ENOENT || errno == ENOTDIR)) 170 | continue; 171 | 172 | i2c_bus_found = true; 173 | 174 | if (fd < 0) { 175 | eprintf("Failed accessing I2C bus %d: %s (%d)\n", 176 | i, strerror(errno), -errno); 177 | continue; 178 | } 179 | 180 | // return success only if i2c bus exists and can be open 181 | ret = 0; 182 | printf("On i2c-%d:\n\t", i); 183 | for (int j = MIN_I2C_ADDR; j <= MAX_I2C_ADDR; j++) 184 | if (i2c_probe(fd, j)) 185 | printf("0x%x ", j); 186 | 187 | printf("\n"); 188 | close(fd); 189 | } 190 | 191 | if (!i2c_bus_found) { 192 | PRINT_NOT_FOUND("I2C device"); 193 | PRINT_BUS_NUM(bus); 194 | PRINT_DRIVER_HINT("i2c-dev"); 195 | } 196 | 197 | return ret; 198 | } 199 | 200 | #define DRIVER_DEV_PATH "/sys/bus/i2c/devices" 201 | static int list_driver_accessible(int bus) 202 | { 203 | ASSERT(bus <= MAX_I2C_BUS); 204 | 205 | int ret = -1; 206 | char *dev_file_format = DRIVER_DEV_PATH"/%d-00%02x/eeprom"; 207 | char dev_file_name[40]; 208 | bool driver_found = false; 209 | 210 | if (access(DRIVER_DEV_PATH, F_OK) < 0) { 211 | eprintf("Failed accessing path %s: %s (%d)\n", 212 | DRIVER_DEV_PATH, strerror(errno), -errno); 213 | return ret; 214 | } 215 | 216 | int i = (bus < 0) ? MIN_I2C_BUS : bus; 217 | int end = (bus < 0) ? MAX_I2C_BUS : bus; 218 | for (; i <= end; i++) { 219 | for (int j = MIN_I2C_ADDR; j <= MAX_I2C_ADDR; j++) { 220 | sprintf(dev_file_name, dev_file_format, i, j); 221 | int res = access(dev_file_name, F_OK); 222 | if (res < 0 && (errno == ENOENT || errno == ENOTDIR)) 223 | continue; 224 | 225 | driver_found = true; 226 | 227 | if (res < 0) { 228 | eprintf("Failed accessing device %s: %s (%d)\n", 229 | dev_file_name, strerror(errno), -errno); 230 | continue; 231 | } 232 | 233 | // return success only if device exists and accessible 234 | ret = 0; 235 | 236 | printf("EEPROM device file found at: %s\n", 237 | dev_file_name); 238 | } 239 | } 240 | 241 | if (!driver_found) { 242 | PRINT_NOT_FOUND("EEPROM device"); 243 | PRINT_BUS_NUM(bus); 244 | PRINT_DRIVER_HINT("EEPROM"); 245 | } 246 | 247 | return ret; 248 | } 249 | 250 | static int list_accessible(struct api *api) 251 | { 252 | ASSERT(api); 253 | 254 | int ret1, ret2; 255 | 256 | printf(COLOR_GREEN "I2C buses:\n" COLOR_RESET); 257 | ret1 = list_i2c_accessible(api->i2c_bus); 258 | printf(COLOR_GREEN "\nEEPROM device files:\n" COLOR_RESET); 259 | ret2 = list_driver_accessible(api->i2c_bus); 260 | 261 | return -(ret1 && ret2); /* return -1 only if both fail */ 262 | } 263 | 264 | static void system_error(const char *message) 265 | { 266 | perror(message); 267 | } 268 | 269 | static int setup_interface(struct api *api) 270 | { 271 | ASSERT(api); 272 | ASSERT(api->i2c_bus >= MIN_I2C_BUS && api->i2c_bus <= MAX_I2C_BUS); 273 | ASSERT(api->i2c_addr >= MIN_I2C_ADDR && api->i2c_addr <= MAX_I2C_ADDR); 274 | 275 | char i2cdev_fname[13]; 276 | char eeprom_dev_fname[40]; 277 | int saved_errno; 278 | 279 | sprintf(i2cdev_fname, "/dev/i2c-%d", api->i2c_bus); 280 | api->fd = open_device_file(i2cdev_fname, api->i2c_addr); 281 | if (api->fd >= 0) { 282 | api->read = i2c_read; 283 | api->write = i2c_write; 284 | return 0; 285 | } 286 | 287 | saved_errno = errno; 288 | 289 | sprintf(eeprom_dev_fname, DRIVER_DEV_PATH"/%d-00%02x/eeprom", 290 | api->i2c_bus, api->i2c_addr); 291 | api->fd = open_device_file(eeprom_dev_fname, -1); 292 | if (api->fd < 0) { 293 | /* print error which occurred when opening i2c-dev file */ 294 | eprintf("Error, %s access failed: %s (%d)\n", 295 | i2cdev_fname, strerror(saved_errno), -saved_errno); 296 | if (saved_errno == ENOENT) 297 | PRINT_DRIVER_HINT("i2c-dev"); 298 | 299 | /* print error which occurred when opening eeprom-dev file */ 300 | eprintf("Error, %s access failed: %s (%d)\n", 301 | eeprom_dev_fname, strerror(errno), -errno); 302 | if (errno == ENOENT) 303 | PRINT_DRIVER_HINT("EEPROM"); 304 | } else { 305 | api->read = driver_read; 306 | api->write = driver_write; 307 | return 0; 308 | } 309 | 310 | eprintf("Neither EEPROM driver nor i2c device interface is available"); 311 | eprintf("\n"); 312 | 313 | return -1; 314 | } 315 | 316 | static int api_read_before_setup(struct api *api, unsigned char *buf, 317 | int offset, int size) 318 | { 319 | ASSERT(api); 320 | 321 | // Setup sets the api->read and api->write function pointers on success 322 | if (setup_interface(api) == 0) 323 | return api->read(api, buf, offset, size); 324 | 325 | return -1; 326 | } 327 | 328 | static int api_write_before_setup(struct api *api, unsigned char *buf, 329 | int offset, int size) 330 | { 331 | ASSERT(api); 332 | 333 | // Setup sets the api->read and api->write function pointers on success 334 | if (setup_interface(api) == 0) 335 | return api->write(api, buf, offset, size); 336 | 337 | return -1; 338 | } 339 | 340 | void api_init(struct api *api, int i2c_bus, int i2c_addr) 341 | { 342 | api->i2c_bus = i2c_bus; 343 | api->i2c_addr = i2c_addr; 344 | 345 | api->read = api_read_before_setup; 346 | api->write = api_write_before_setup; 347 | api->probe = list_accessible; 348 | api->system_error = system_error; 349 | } 350 | -------------------------------------------------------------------------------- /layout.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2017 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * Igor Grinberg 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "layout.h" 26 | #include "common.h" 27 | #include "field.h" 28 | 29 | #define LAYOUT_CHECK_BYTE 44 30 | #define NO_LAYOUT_FIELDS "Unknown layout. Dumping raw data\n" 31 | #define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0])) 32 | 33 | struct field layout_legacy[5] = { 34 | { "MAC address", "mac", 6, FIELD_MAC }, 35 | { "Board Revision", "rev", 2, FIELD_BINARY }, 36 | { "Serial Number", "sn", 8, FIELD_BINARY }, 37 | { "Board Configuration", "conf", 64, FIELD_ASCII }, 38 | { "Reserved fields", "rsvd", 176, FIELD_RESERVED }, 39 | }; 40 | 41 | struct field layout_v1[12] = { 42 | { "Major Revision", "major", 2, FIELD_VERSION }, 43 | { "Minor Revision", "minor", 2, FIELD_VERSION }, 44 | { "1st MAC Address", "mac1", 6, FIELD_MAC }, 45 | { "2nd MAC Address", "mac2", 6, FIELD_MAC }, 46 | { "Production Date", "date", 4, FIELD_DATE }, 47 | { "Serial Number", "sn", 12, FIELD_REVERSED }, 48 | { "Reserved fields", "rsvd", 96, FIELD_RESERVED }, 49 | { "Product Name", "name", 16, FIELD_ASCII }, 50 | { "Product Options #1", "opt1", 16, FIELD_ASCII }, 51 | { "Product Options #2", "opt2", 16, FIELD_ASCII }, 52 | { "Product Options #3", "opt3", 16, FIELD_ASCII }, 53 | { "Reserved fields", "rsvd", 64, FIELD_RESERVED }, 54 | }; 55 | 56 | struct field layout_v2[15] = { 57 | { "Major Revision", "major", 2, FIELD_VERSION }, 58 | { "Minor Revision", "minor", 2, FIELD_VERSION }, 59 | { "1st MAC Address", "mac1", 6, FIELD_MAC }, 60 | { "2nd MAC Address", "mac2", 6, FIELD_MAC }, 61 | { "Production Date", "date", 4, FIELD_DATE }, 62 | { "Serial Number", "sn", 12, FIELD_REVERSED }, 63 | { "3rd MAC Address (WIFI)", "mac3", 6, FIELD_MAC }, 64 | { "4th MAC Address (Bluetooth)", "mac4", 6, FIELD_MAC }, 65 | { "Layout Version", "layout", 1, FIELD_BINARY }, 66 | { "Reserved fields", "rsvd", 83, FIELD_RESERVED }, 67 | { "Product Name", "name", 16, FIELD_ASCII }, 68 | { "Product Options #1", "opt1", 16, FIELD_ASCII }, 69 | { "Product Options #2", "opt2", 16, FIELD_ASCII }, 70 | { "Product Options #3", "opt3", 16, FIELD_ASCII }, 71 | { "Reserved fields", "rsvd", 64, FIELD_RESERVED }, 72 | }; 73 | 74 | struct field layout_v3[16] = { 75 | { "Major Revision", "major", 2, FIELD_VERSION }, 76 | { "Minor Revision", "minor", 2, FIELD_VERSION }, 77 | { "1st MAC Address", "mac1", 6, FIELD_MAC }, 78 | { "2nd MAC Address", "mac2", 6, FIELD_MAC }, 79 | { "Production Date", "date", 4, FIELD_DATE }, 80 | { "Serial Number", "sn", 12, FIELD_REVERSED }, 81 | { "3rd MAC Address (WIFI)", "mac3", 6, FIELD_MAC }, 82 | { "4th MAC Address (Bluetooth)", "mac4", 6, FIELD_MAC }, 83 | { "Layout Version", "layout", 1, FIELD_BINARY }, 84 | { "CompuLab EEPROM ID", "id", 3, FIELD_BINARY }, 85 | { "Reserved fields", "rsvd", 80, FIELD_RESERVED }, 86 | { "Product Name", "name", 16, FIELD_ASCII }, 87 | { "Product Options #1", "opt1", 16, FIELD_ASCII }, 88 | { "Product Options #2", "opt2", 16, FIELD_ASCII }, 89 | { "Product Options #3", "opt3", 16, FIELD_ASCII }, 90 | { "Reserved fields", "rsvd", 64, FIELD_RESERVED }, 91 | }; 92 | 93 | struct field layout_v4[21] = { 94 | { "Major Revision", "major", 2, FIELD_VERSION }, 95 | { "Minor Revision", "minor", 2, FIELD_VERSION }, 96 | { "1st MAC Address", "mac1", 6, FIELD_MAC }, 97 | { "2nd MAC Address", "mac2", 6, FIELD_MAC }, 98 | { "Production Date", "date", 4, FIELD_DATE }, 99 | { "Serial Number", "sn", 12, FIELD_REVERSED }, 100 | { "3rd MAC Address (WIFI)", "mac3", 6, FIELD_MAC }, 101 | { "4th MAC Address (Bluetooth)", "mac4", 6, FIELD_MAC }, 102 | { "Layout Version", "layout", 1, FIELD_BINARY }, 103 | { "CompuLab EEPROM ID", "id", 3, FIELD_BINARY }, 104 | { "5th MAC Address", "mac5", 6, FIELD_MAC }, 105 | { "6th MAC Address", "mac6", 6, FIELD_MAC }, 106 | { "Scratchpad", "spad", 4, FIELD_BINARY }, 107 | { "Reserved fields", "rsvd", 64, FIELD_RESERVED }, 108 | { "Product Name", "name", 16, FIELD_ASCII }, 109 | { "Product Options #1", "opt1", 16, FIELD_ASCII }, 110 | { "Product Options #2", "opt2", 16, FIELD_ASCII }, 111 | { "Product Options #3", "opt3", 16, FIELD_ASCII }, 112 | { "Product Options #4", "opt4", 16, FIELD_ASCII }, 113 | { "Product Options #5", "opt5", 16, FIELD_ASCII }, 114 | { "Reserved fields", "rsvd", 32, FIELD_RESERVED }, 115 | }; 116 | 117 | struct field layout_unknown[1] = { 118 | { NO_LAYOUT_FIELDS, "raw", 256, FIELD_RAW }, 119 | }; 120 | 121 | /* 122 | * detect_layout() - detect layout based on the contents of the data. 123 | * @data: Pointer to the data to be analyzed. 124 | * 125 | * Returns: the detected layout version. 126 | */ 127 | static enum layout_version detect_layout(unsigned char *data) 128 | { 129 | ASSERT(data); 130 | 131 | switch (data[LAYOUT_CHECK_BYTE]) { 132 | case 0xff: 133 | case 0: 134 | return LAYOUT_VER1; 135 | case 2: 136 | return LAYOUT_VER2; 137 | case 3: 138 | return LAYOUT_VER3; 139 | case 4: 140 | return LAYOUT_VER4; 141 | } 142 | 143 | if (data[LAYOUT_CHECK_BYTE] >= 0x20) 144 | return LAYOUT_LEGACY; 145 | 146 | return LAYOUT_UNRECOGNIZED; 147 | } 148 | 149 | /* 150 | * build_layout() - Detect layout and build it with a predefined array 151 | * @layout: An allocated layout 152 | */ 153 | static void build_layout(struct layout *layout) 154 | { 155 | if (layout->layout_version == LAYOUT_AUTODETECT) 156 | layout->layout_version = detect_layout(layout->data); 157 | 158 | switch (layout->layout_version) { 159 | case LAYOUT_LEGACY: 160 | layout->fields = layout_legacy; 161 | layout->num_of_fields = ARRAY_LEN(layout_legacy); 162 | break; 163 | case LAYOUT_VER1: 164 | layout->fields = layout_v1; 165 | layout->num_of_fields = ARRAY_LEN(layout_v1); 166 | break; 167 | case LAYOUT_VER2: 168 | layout->fields = layout_v2; 169 | layout->num_of_fields = ARRAY_LEN(layout_v2); 170 | break; 171 | case LAYOUT_VER3: 172 | layout->fields = layout_v3; 173 | layout->num_of_fields = ARRAY_LEN(layout_v3); 174 | break; 175 | case LAYOUT_VER4: 176 | layout->fields = layout_v4; 177 | layout->num_of_fields = ARRAY_LEN(layout_v4); 178 | break; 179 | default: 180 | layout->fields = layout_unknown; 181 | layout->num_of_fields = ARRAY_LEN(layout_unknown); 182 | } 183 | } 184 | 185 | /* 186 | * print_layout() - print the layout and the data which is assigned to it. 187 | * @layout: A pointer to an existing struct layout. 188 | */ 189 | static void print_layout(const struct layout *layout) 190 | { 191 | ASSERT(layout && layout->fields); 192 | 193 | struct field *fields = layout->fields; 194 | 195 | for (int i = 0; i < layout->num_of_fields; i++) 196 | fields[i].ops->print(&fields[i]); 197 | } 198 | 199 | /* 200 | * offset_to_string() - convert offset or range to string 201 | * @dest_str: A pointer to where the string will be written 202 | * @offset_start: The start offset 203 | * @offset_end: The end offset 204 | */ 205 | static void offset_to_string(char* dest_str, int offset_start, int offset_end) 206 | { 207 | ASSERT(dest_str); 208 | int chars = sprintf(dest_str, "'0x%02x", offset_start); 209 | if (offset_end != offset_start) 210 | chars += sprintf(dest_str + chars, "-0x%02x", offset_end); 211 | sprintf(dest_str + chars, "'"); 212 | } 213 | 214 | /* 215 | * get_bytes_range() - Test offsets values and return range 216 | * @offset_start: The start offset 217 | * @offset_end: The end offset 218 | * 219 | * Returns: range on success, 0 on failure. 220 | */ 221 | static size_t get_bytes_range(int offset_start, int offset_end) 222 | { 223 | if (offset_start < 0 || offset_start >= EEPROM_SIZE || 224 | offset_end < offset_start || offset_end >= EEPROM_SIZE) { 225 | char offset_str[30]; 226 | offset_to_string(offset_str, offset_start, offset_end); 227 | ieprintf("Invalid offset %s", offset_str); 228 | return 0; 229 | } 230 | 231 | return offset_end - offset_start + 1; 232 | } 233 | 234 | /* 235 | * Selectively update EEPROM data by bytes. 236 | * @layout: An initialized layout. 237 | * @data: A data array. Each element contains the following: 238 | * start: The first byte in EEPROM to be written. 239 | * end: The last byte in EEPROM to be written. 240 | * value: The value to be written to EEPROM. 241 | * 242 | * Returns: number of updated bytes. 243 | */ 244 | static int update_bytes(struct layout *layout, struct data_array *data) 245 | { 246 | ASSERT(layout && data && data->bytes_changes); 247 | 248 | int updated_bytes = 0; 249 | 250 | for (int i = 0; i < data->size; i++) { 251 | int offset_start = data->bytes_changes[i].start; 252 | int offset_end = data->bytes_changes[i].end; 253 | size_t range = get_bytes_range(offset_start, offset_end); 254 | if (range == 0) 255 | return 0; 256 | 257 | int value = data->bytes_changes[i].value; 258 | if (value >= 0 && value <= 255){ 259 | memset(layout->data + offset_start, value, range); 260 | updated_bytes += range; 261 | continue; 262 | } 263 | 264 | char value_str[60]; 265 | int chars = sprintf(value_str, "'0x%02x' at offset ", value); 266 | offset_to_string(value_str + chars, offset_start, offset_end); 267 | ieprintf("Invalid value %s", value_str); 268 | return 0; 269 | } 270 | 271 | return updated_bytes; 272 | } 273 | 274 | /* 275 | * Selectively clear EEPROM data by bytes. 276 | * @layout: An initialized layout. 277 | * @data: A data array. Each element contains the following: 278 | * start: The first byte in EEPROM to be cleared. 279 | * end: The last byte in EEPROM to be cleared. 280 | * 281 | * Returns: number of cleared bytes. 282 | */ 283 | static int clear_bytes(struct layout *layout, struct data_array *data) 284 | { 285 | ASSERT(layout && data && data->bytes_list); 286 | 287 | int cleared_bytes = 0; 288 | 289 | for (int i = 0; i < data->size; i++) { 290 | int offset_start = data->bytes_list[i].start; 291 | int offset_end = data->bytes_list[i].end; 292 | size_t range = get_bytes_range(offset_start, offset_end); 293 | if (range == 0) 294 | return 0; 295 | 296 | memset(layout->data + offset_start, 0xff, range); 297 | cleared_bytes += range; 298 | } 299 | 300 | return cleared_bytes; 301 | } 302 | 303 | /* 304 | * find_field() - Find a field by name from the layout data. 305 | * @layout: An initialized layout 306 | * @field_name: The name of the field to find 307 | * 308 | * Returns: A pointer to the field on success, NULL on failure. 309 | */ 310 | static struct field* find_field(struct layout *layout, char *field_name) 311 | { 312 | ASSERT(layout && layout->fields && field_name); 313 | 314 | struct field *fields = layout->fields; 315 | 316 | if (fields == layout_unknown) { 317 | eprintf("Layout error: Can't operate on fields. " 318 | "The layout is unknown.\n"); 319 | return NULL; 320 | } 321 | 322 | for (int i = 0; i < layout->num_of_fields; i++) 323 | if (fields[i].ops->is_named(&fields[i], field_name)) 324 | return &fields[i]; 325 | 326 | ieprintf("Field \"%s\" not found", field_name); 327 | 328 | return NULL; 329 | } 330 | 331 | /* 332 | * Selectively update EEPROM data by fields. 333 | * @layout: An initialized layout. 334 | * @data: A data array. Each element contains field and value strings 335 | * 336 | * Returns: number of updated fields. 337 | */ 338 | static int update_fields(struct layout *layout, struct data_array *data) 339 | { 340 | ASSERT(data && data->fields_changes); 341 | 342 | int updated_fields_cnt = 0; 343 | 344 | for (int i = 0; i < data->size; i++) { 345 | char *field_name = data->fields_changes[i].field; 346 | char *field_value = data->fields_changes[i].value; 347 | 348 | struct field *field = find_field(layout, field_name); 349 | if (!field) 350 | return 0; 351 | 352 | if (*field_value == '\0') 353 | field->ops->clear(field); 354 | else if (field->ops->update(field, field_value)) 355 | return 0; 356 | 357 | updated_fields_cnt++; 358 | } 359 | 360 | return updated_fields_cnt; 361 | } 362 | 363 | /* 364 | * clear_fields() - Selectively clear EEPROM data by fields. 365 | * @layout: An initialized layout 366 | * @data: A data array. Each element contains field name string 367 | * 368 | * Returns: number of cleared fields. 369 | */ 370 | static int clear_fields(struct layout *layout, struct data_array *data) 371 | { 372 | ASSERT(data && data->fields_list); 373 | 374 | int cleared_fields_cnt = 0; 375 | 376 | for (int i = 0; i < data->size; i++) { 377 | struct field *field = find_field(layout, data->fields_list[i]); 378 | if (!field) 379 | return 0; 380 | 381 | field->ops->clear(field); 382 | cleared_fields_cnt++; 383 | } 384 | 385 | return cleared_fields_cnt; 386 | } 387 | 388 | /* 389 | * new_layout() - Allocate a new layout based on the data given in buf. 390 | * @buf: Data seed for layout 391 | * @buf_size: Size of buf 392 | * 393 | * Allocates a new layout based on data in buf. The layout version is 394 | * automatically detected. The resulting layout struct contains a copy of the 395 | * provided data. 396 | * 397 | * Returns: pointer to a new layout on success, NULL on failure 398 | */ 399 | struct layout *new_layout(unsigned char *buf, unsigned int buf_size, 400 | enum layout_version layout_version, 401 | enum print_format print_format) 402 | { 403 | ASSERT(buf); 404 | 405 | struct layout *layout = malloc(sizeof(struct layout)); 406 | if (!layout) 407 | return NULL; 408 | 409 | layout->layout_version = layout_version; 410 | layout->data = buf; 411 | layout->data_size = buf_size; 412 | 413 | build_layout(layout); 414 | 415 | for (int i = 0; i < layout->num_of_fields; i++) { 416 | struct field *field = &layout->fields[i]; 417 | init_field(field, buf, print_format); 418 | buf += field->ops->get_data_size(field); 419 | } 420 | 421 | layout->print = print_layout; 422 | layout->update_fields = update_fields; 423 | layout->update_bytes = update_bytes; 424 | layout->clear_fields = clear_fields; 425 | layout->clear_bytes = clear_bytes; 426 | 427 | return layout; 428 | } 429 | 430 | /* 431 | * free_layout() - a destructor for layout 432 | * @layout: the layout to deallocate 433 | */ 434 | void free_layout(struct layout *layout) 435 | { 436 | free(layout); 437 | } 438 | -------------------------------------------------------------------------------- /field.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2011 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * Igor Grinberg 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "common.h" 25 | #include "field.h" 26 | 27 | // Macro for printing field's input value error messages 28 | #define iveprintf(str, value, name) \ 29 | ieprintf("Invalid value \"%s\" for field \"%s\" - " str, value, name); 30 | 31 | static void __print_bin(const struct field *field, 32 | char *delimiter, bool reverse) 33 | { 34 | ASSERT(field && field->data && delimiter); 35 | 36 | int i; 37 | int from = reverse ? field->data_size - 1 : 0; 38 | int to = reverse ? 0 : field->data_size - 1; 39 | for (i = from; i != to; reverse ? i-- : i++) 40 | printf("%02x%s", field->data[i], delimiter); 41 | 42 | printf("%02x\n", field->data[i]); 43 | } 44 | 45 | static int __update_bin(struct field *field, const char *value, bool reverse) 46 | { 47 | ASSERT(field && field->data && field->name && value); 48 | 49 | int len = strlen(value); 50 | int i = reverse ? len - 1 : 0; 51 | 52 | /* each two characters in the string are fit in one byte */ 53 | if (len > field->data_size * 2) { 54 | iveprintf("Value is too long", value, field->name); 55 | return -1; 56 | } 57 | 58 | /* pad with zeros */ 59 | memset(field->data, 0, field->data_size); 60 | 61 | /* i - string iterator, j - data iterator */ 62 | for (int j = 0; j < field->data_size; j++) { 63 | int byte = 0; 64 | char tmp[3] = { 0, 0, 0 }; 65 | 66 | if ((reverse && i < 0) || (!reverse && i >= len)) 67 | break; 68 | 69 | for (int k = 0; k < 2; k++) { 70 | if (reverse && i == 0) { 71 | tmp[k] = value[i]; 72 | break; 73 | } 74 | 75 | tmp[k] = value[reverse ? i - 1 + k : i + k]; 76 | } 77 | 78 | char *str = tmp; 79 | if (strtoi_base(&str, &byte, 16) < 0 || byte < 0 || byte >> 8) { 80 | iveprintf("Syntax error", value, field->name); 81 | return -1; 82 | } 83 | 84 | field->data[j] = (unsigned char)byte; 85 | i = reverse ? i - 2 : i + 2; 86 | } 87 | 88 | return 0; 89 | } 90 | 91 | static int __update_bin_delim(struct field *field, char *value, char delimiter) 92 | { 93 | ASSERT(field && field->data && field->name && value); 94 | 95 | int i, val; 96 | char *bin = value; 97 | 98 | for (i = 0; i < (field->data_size - 1); i++) { 99 | if (strtoi_base(&bin, &val, 16) != STRTOI_STR_CON || 100 | *bin != delimiter || val < 0 || val >> 8) { 101 | iveprintf("Syntax error", value, field->name); 102 | return -1; 103 | } 104 | 105 | field->data[i] = (unsigned char)val; 106 | bin++; 107 | } 108 | 109 | if (strtoi_base(&bin, &val, 16) != STRTOI_STR_END || 110 | val < 0 || val >> 8) { 111 | iveprintf("Syntax error", value, field->name); 112 | return -1; 113 | } 114 | 115 | field->data[i] = (unsigned char)val; 116 | 117 | return 0; 118 | } 119 | 120 | /** 121 | * print_bin() - print the value of a field from type "binary" 122 | * 123 | * Treat the field data as simple binary data, and print it as two digit 124 | * hexadecimal values. 125 | * Sample output: 0102030405060708090a 126 | * 127 | * @field: an initialized field to print 128 | */ 129 | static void print_bin(const struct field *field) 130 | { 131 | __print_bin(field, "", false); 132 | } 133 | 134 | /** 135 | * print_bin_raw() - print raw data both in hexadecimal and in ascii format 136 | * 137 | * @field: an initialized field to print 138 | */ 139 | static void print_bin_raw(const struct field *field) 140 | { 141 | ASSERT(field && field->data); 142 | 143 | printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f" 144 | " 0123456789abcdef\n"); 145 | int i, j; 146 | 147 | for (i = 0; i < 256; i += 16) { 148 | printf("%02x: ", i); 149 | for (j = 0; j < 16; j++) { 150 | printf("%02x", field->data[i+j]); 151 | printf(" "); 152 | } 153 | printf(" "); 154 | 155 | for (j = 0; j < 16; j++) { 156 | if (field->data[i+j] == 0x00 || field->data[i+j] == 0xff) 157 | printf("."); 158 | else if (field->data[i+j] < 32 || field->data[i+j] >= 127) 159 | printf("?"); 160 | else 161 | printf("%c", field->data[i+j]); 162 | } 163 | printf("\n"); 164 | } 165 | } 166 | 167 | /** 168 | * update_bin() - Update field with new data in binary form 169 | * 170 | * @field: an initialized field 171 | * @value: a string of values (i.e. "10b234a") 172 | */ 173 | static int update_bin(struct field *field, char *value) 174 | { 175 | return __update_bin(field, value, false); 176 | } 177 | 178 | /** 179 | * print_bin_rev() - print the value of a field from type "reversed" 180 | * 181 | * Treat the field data as simple binary data, and print it in reverse order 182 | * as two digit hexadecimal values. 183 | * 184 | * Data in field: 0102030405060708090a 185 | * Sample output: 0a090807060504030201 186 | * 187 | * @field: an initialized field to print 188 | */ 189 | static void print_bin_rev(const struct field *field) 190 | { 191 | __print_bin(field, "", true); 192 | } 193 | 194 | /** 195 | * update_bin_rev() - Update field with new data in binary form, storing it in 196 | * reverse 197 | * 198 | * This function takes a string of byte values, and stores them 199 | * in the field in the reverse order. i.e. if the input string was "1234", 200 | * "3412" will be written to the field. 201 | * 202 | * @field: an initialized field 203 | * @value: a string of byte values 204 | */ 205 | static int update_bin_rev(struct field *field, char *value) 206 | { 207 | return __update_bin(field, value, true); 208 | } 209 | 210 | /** 211 | * print_bin_ver() - print the value of a field from type "version" 212 | * 213 | * Treat the field data as simple binary data, and print it formatted as a 214 | * version number (2 digits after decimal point). 215 | * The field size must be exactly 2 bytes. 216 | * 217 | * Sample output: 123.45 218 | * 219 | * @field: an initialized field to print 220 | */ 221 | static void print_bin_ver(const struct field *field) 222 | { 223 | ASSERT(field && field->data); 224 | 225 | if ((field->data[0] == 0xff) && (field->data[1] == 0xff)) { 226 | field->data[0] = 0; 227 | field->data[1] = 0; 228 | } 229 | 230 | printf("%#.2f\n", (field->data[1] << 8 | field->data[0]) / 100.0); 231 | } 232 | 233 | /** 234 | * update_bin_ver() - update a "version field" which contains binary data 235 | * 236 | * This function takes a version string in the form of x.y (x and y are both 237 | * decimal values, y is limited to two digits), translates it to the binary 238 | * form, then writes it to the field. The field size must be exactly 2 bytes. 239 | * 240 | * This function strictly enforces the data syntax, and will not update the 241 | * field if there's any deviation from it. It also protects from overflow. 242 | * 243 | * @field: an initialized field 244 | * @value: a version string 245 | * 246 | * Returns 0 on success, -1 on failure. 247 | */ 248 | static int update_bin_ver(struct field *field, char *value) 249 | { 250 | ASSERT(field && field->data && field->name && value); 251 | 252 | char *version = value; 253 | int num, remainder; 254 | 255 | if (strtoi(&version, &num) != STRTOI_STR_CON && *version != '.') { 256 | iveprintf("Syntax error", value, field->name); 257 | return -1; 258 | } 259 | 260 | version++; 261 | if (strtoi(&version, &remainder) != STRTOI_STR_END) { 262 | iveprintf("Syntax error", value, field->name); 263 | return -1; 264 | } 265 | 266 | if (num < 0 || remainder < 0) { 267 | iveprintf("Version must be positive", value, field->name); 268 | return -1; 269 | } 270 | 271 | if (remainder > 99) { 272 | iveprintf("Minor version is 1-2 digits", value, field->name); 273 | return -1; 274 | } 275 | 276 | num = num * 100 + remainder; 277 | if (num >> 16) { 278 | iveprintf("Version is too big", value, field->name); 279 | return -1; 280 | } 281 | 282 | field->data[0] = (unsigned char)num; 283 | field->data[1] = num >> 8; 284 | 285 | return 0; 286 | } 287 | 288 | /** 289 | * print_mac_addr() - print the value of a field from type "mac" 290 | * 291 | * Treat the field data as simple binary data, and print it formatted as a MAC 292 | * address. 293 | * Sample output: 01:02:03:04:05:06 294 | * 295 | * @field: an initialized field to print 296 | */ 297 | static void print_mac(const struct field *field) 298 | { 299 | __print_bin(field, ":", false); 300 | } 301 | 302 | /** 303 | * update_mac() - Update a mac address field which contains binary data 304 | * 305 | * @field: an initialized field 306 | * @value: a colon delimited string of byte values (i.e. "1:02:3:ff") 307 | */ 308 | static int update_mac(struct field *field, char *value) 309 | { 310 | return __update_bin_delim(field, value, ':'); 311 | } 312 | 313 | static char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 314 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; 315 | 316 | /** 317 | * print_date() - print the value of a field from type "date" 318 | * 319 | * Treat the field data as simple binary data, and print it formatted as a date. 320 | * Sample output: 07/Feb/2014 321 | * 56/BAD/9999 322 | * 323 | * @field: an initialized field to print 324 | */ 325 | static void print_date(const struct field *field) 326 | { 327 | ASSERT(field && field->data); 328 | 329 | printf("%02d/", field->data[0]); 330 | if (field->data[1] >= 1 && field->data[1] <= 12) 331 | printf("%s", months[field->data[1] - 1]); 332 | else 333 | printf("BAD"); 334 | 335 | printf("/%d\n", field->data[3] << 8 | field->data[2]); 336 | } 337 | 338 | static int validate_date(unsigned char day, unsigned char month, 339 | unsigned int year) 340 | { 341 | int days_in_february; 342 | 343 | switch (month) { 344 | case 0: 345 | case 2: 346 | case 4: 347 | case 6: 348 | case 7: 349 | case 9: 350 | case 11: 351 | if (day > 31) 352 | return -1; 353 | break; 354 | case 3: 355 | case 5: 356 | case 8: 357 | case 10: 358 | if (day > 30) 359 | return -1; 360 | break; 361 | case 1: 362 | days_in_february = 28; 363 | if (year % 4 == 0) { 364 | if (year % 100 != 0) { 365 | days_in_february = 29; 366 | } else if (year % 400 == 0) { 367 | days_in_february = 29; 368 | } 369 | } 370 | 371 | if (day > days_in_february) 372 | return -1; 373 | 374 | break; 375 | default: 376 | return -1; 377 | } 378 | 379 | return 0; 380 | } 381 | 382 | /** 383 | * update_date() - update a date field which contains binary data 384 | * 385 | * This function takes a date string in the form of x/Mon/y (x and y are both 386 | * decimal values), translates it to the binary representation, then writes it 387 | * to the field. 388 | * 389 | * This function strictly enforces the data syntax, and will not update the 390 | * field if there's any deviation from it. It also protects from overflow in the 391 | * year value, and checks the validity of the date. 392 | * 393 | * @field: an initialized field 394 | * @value: a date string 395 | * 396 | * Returns 0 on success, -1 on failure. 397 | */ 398 | static int update_date(struct field *field, char *value) 399 | { 400 | ASSERT(field && field->data && field->name && value); 401 | 402 | char *date = value; 403 | int day, month, year; 404 | 405 | if (strtoi(&date, &day) != STRTOI_STR_CON || *date != '/') { 406 | iveprintf("Syntax error", value, field->name); 407 | return -1; 408 | } 409 | 410 | if (day == 0) { 411 | iveprintf("Invalid day", value, field->name); 412 | return -1; 413 | } 414 | 415 | date++; 416 | if (strlen(date) < 4 || *(date + 3) != '/') { 417 | iveprintf("Syntax error", value, field->name); 418 | return -1; 419 | } 420 | 421 | for (month = 1; month <= 12; month++) 422 | if (!strncmp(date, months[month - 1], 3)) 423 | break; 424 | 425 | if (strncmp(date, months[month - 1], 3)) { 426 | iveprintf("Invalid month", value, field->name); 427 | return -1; 428 | } 429 | 430 | date += 4; 431 | if (strtoi(&date, &year) != STRTOI_STR_END) { 432 | iveprintf("Syntax error", value, field->name); 433 | return -1; 434 | } 435 | 436 | if (validate_date(day, month - 1, year)) { 437 | iveprintf("Invalid date", value, field->name); 438 | return -1; 439 | } 440 | 441 | if (year >> 16) { 442 | iveprintf("Year overflow", value, field->name); 443 | return -1; 444 | } 445 | 446 | field->data[0] = (unsigned char)day; 447 | field->data[1] = (unsigned char)month; 448 | field->data[2] = (unsigned char)year; 449 | field->data[3] = (unsigned char)(year >> 8); 450 | 451 | return 0; 452 | } 453 | 454 | /** 455 | * print_ascii() - print the value of a field from type "ascii" 456 | * @field: an initialized field to print 457 | */ 458 | static void print_ascii(const struct field *field) 459 | { 460 | ASSERT(field && field->data); 461 | 462 | char format[8]; 463 | int *str = (int*)field->data; 464 | int pattern = *str; 465 | /* assuming field->data_size is a multiple of 32bit! */ 466 | int block_count = field->data_size / sizeof(int); 467 | char *print_buf = ""; 468 | 469 | /* check if str is trivial (contains only 0's or only 0xff's), if so print nothing */ 470 | for (int i = 0; i < block_count - 1; i++) { 471 | str++; 472 | if (*str != pattern || (pattern != 0 && pattern != -1)) { 473 | print_buf = (char*)field->data; 474 | break; 475 | } 476 | } 477 | 478 | sprintf(format, "%%.%ds\n", field->data_size); 479 | printf(format, print_buf); 480 | } 481 | 482 | /** 483 | * update_ascii() - Update field with new data in ASCII form 484 | * @field: an initialized field 485 | * @value: the new string data 486 | * 487 | * Returns 0 on success, -1 of failure (new string too long). 488 | */ 489 | static int update_ascii(struct field *field, char *value) 490 | { 491 | ASSERT(field && field->data && field->name && value); 492 | 493 | if (strlen(value) >= field->data_size) { 494 | iveprintf("Value is too long", value, field->name); 495 | return -1; 496 | } 497 | 498 | strncpy((char *)field->data, value, field->data_size - 1); 499 | field->data[field->data_size - 1] = '\0'; 500 | 501 | return 0; 502 | } 503 | 504 | /** 505 | * print_reserved() - print the size of a field from type "reserved" 506 | * 507 | * Print a notice that the following field_size bytes are reserved. 508 | * 509 | * Sample output: (64 bytes) 510 | * 511 | * @field: an initialized field to print 512 | */ 513 | static void print_reserved(const struct field *field) 514 | { 515 | ASSERT(field); 516 | printf("(%d bytes)\n", field->data_size); 517 | } 518 | 519 | /** 520 | * clear_field() - clear a field 521 | * 522 | * A cleared field is defined by having all bytes set to 0xff. 523 | * 524 | * @field: an initialized field to clear 525 | */ 526 | static void clear_field(struct field *field) 527 | { 528 | ASSERT(field && field->data); 529 | memset(field->data, 0xff, field->data_size); 530 | } 531 | 532 | /** 533 | * get_data_size() - get the size of field's data 534 | * 535 | * @field: an initialized field 536 | * 537 | * return: the size of field's data 538 | */ 539 | static int get_data_size(const struct field *field) 540 | { 541 | ASSERT(field); 542 | return field->data_size; 543 | } 544 | 545 | /** 546 | * is_named() - check if any of the field's names match the given string 547 | * 548 | * @field: an initialized field to check 549 | * @str: the string to check 550 | * 551 | * Returns: true if field's names matches, false otherwise. 552 | */ 553 | static bool is_named(const struct field *field, const char *str) 554 | { 555 | ASSERT(field && field->name && field->short_name && str); 556 | 557 | if (field->type != FIELD_RESERVED && field->type != FIELD_RAW && 558 | (!strcmp(field->name, str) || !strcmp(field->short_name, str))) 559 | return true; 560 | 561 | return false; 562 | } 563 | 564 | /** 565 | * print_field() - print the given field using the given string format 566 | * 567 | * @field: an initialized field to to print 568 | * @format: the string format for printf() 569 | */ 570 | static void print_field(const struct field *field, char *format) 571 | { 572 | ASSERT(field && field->name && field->ops && format); 573 | 574 | printf(format, field->name); 575 | field->ops->print_value(field); 576 | } 577 | 578 | /** 579 | * print_default() - print the given field using the default format 580 | * 581 | * @field: an initialized field to to print 582 | */ 583 | static void print_default(const struct field *field) 584 | { 585 | print_field(field, "%-30s"); 586 | } 587 | 588 | /** 589 | * print_dump() - print the given field using the dump format 590 | * 591 | * @field: an initialized field to dump 592 | */ 593 | static void print_dump(const struct field *field) 594 | { 595 | if (field->type != FIELD_RESERVED) 596 | print_field(field, "%s="); 597 | } 598 | 599 | #define OPS_UPDATABLE(type) { \ 600 | .get_data_size = get_data_size, \ 601 | .is_named = is_named, \ 602 | .print_value = print_##type, \ 603 | .print = print_default, \ 604 | .update = update_##type, \ 605 | .clear = clear_field, \ 606 | } 607 | 608 | #define OPS_PRINTABLE(type) { \ 609 | .get_data_size = get_data_size, \ 610 | .is_named = is_named, \ 611 | .print_value = print_##type, \ 612 | .print = print_default, \ 613 | .update = NULL, \ 614 | .clear = NULL, \ 615 | } 616 | 617 | static struct field_ops field_ops[] = { 618 | [FIELD_BINARY] = OPS_UPDATABLE(bin), 619 | [FIELD_REVERSED] = OPS_UPDATABLE(bin_rev), 620 | [FIELD_VERSION] = OPS_UPDATABLE(bin_ver), 621 | [FIELD_ASCII] = OPS_UPDATABLE(ascii), 622 | [FIELD_MAC] = OPS_UPDATABLE(mac), 623 | [FIELD_DATE] = OPS_UPDATABLE(date), 624 | [FIELD_RESERVED] = OPS_PRINTABLE(reserved), 625 | [FIELD_RAW] = OPS_PRINTABLE(bin_raw) 626 | }; 627 | 628 | /** 629 | * init_field() - init field according to field.type 630 | * 631 | * @field: an initialized field with a known field.type to init 632 | * @data: the binary data of the field 633 | * @print_format: the print format of the field 634 | */ 635 | void init_field(struct field *field, unsigned char *data, 636 | enum print_format print_format) 637 | { 638 | ASSERT(field && data); 639 | 640 | field->ops = &field_ops[field->type]; 641 | field->data = data; 642 | 643 | if (print_format == FORMAT_DUMP) 644 | field->ops->print = print_dump; 645 | } 646 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /parser.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2017 CompuLab, Ltd. 3 | * Authors: Nikita Kiryanov 4 | * Igor Grinberg 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "common.h" 28 | #include "command.h" 29 | #include "auto_generated.h" 30 | 31 | #ifdef ENABLE_WRITE 32 | static inline bool write_enabled(void) { return true; } 33 | #else 34 | static inline bool write_enabled(void) { return false; } 35 | #endif 36 | 37 | void print_banner(void) 38 | { 39 | char *version = strnlen(VERSION, 20) ? " version " VERSION : ""; 40 | char *date = " (" BUILD_DATE " - " BUILD_TIME ")"; 41 | 42 | printf("CompuLab EEPROM utility%s%s\n\n", version, date); 43 | } 44 | 45 | static void print_help(void) 46 | { 47 | print_banner(); 48 | printf("Usage: eeprom-util list []\n"); 49 | printf(" eeprom-util read [-f ] [-l ] \n"); 50 | 51 | 52 | if (write_enabled()) { 53 | printf(" eeprom-util write (fields|bytes) [-l ] DATA\n"); 54 | printf(" eeprom-util clear [fields|bytes|all] [DATA]\n"); 55 | } 56 | 57 | printf(" eeprom-util version|-v|--version\n"); 58 | printf(" eeprom-util [help|-h|--help]\n"); 59 | 60 | printf("\n" 61 | "COMMANDS\n" 62 | " list List device addresses accessible via i2c\n" 63 | " read Read from EEPROM\n"); 64 | 65 | if (write_enabled()) { 66 | printf(" write Write to EEPROM. Must specify if writing to 'fields' or 'bytes'\n"); 67 | printf(" clear Clear EEPROM. Default is 'all'. Other options are clearing 'fields' or 'bytes'.\n"); 68 | } 69 | 70 | printf(" version Print the version banner and exit\n" 71 | " help Print this help and exit\n"); 72 | printf("\n" 73 | "LAYOUT VERSIONS\n" 74 | " The -l option can be used to force the utility to interpret the EEPROM data using the chosen layout.\n" 75 | " If the -l option is omitted, the utility will auto detect the layout based on the data in the EEPROM.\n" 76 | " The following values can be provided with the -l option:\n" 77 | " auto use auto-detection to print layout\n" 78 | " legacy, 1, 2, 3, 4 print according to layout version\n" 79 | " raw print raw data\n"); 80 | printf("\n" 81 | "PRINT FORMAT\n" 82 | " The following values can be provided with the -f option:\n" 83 | " default use the default user friendly output\n" 84 | " dump dump the data (usable for later input using \"write fields\")\n"); 85 | 86 | if (write_enabled()) { 87 | printf("\n" 88 | "DATA FORMAT\n" 89 | " Some commands require additional data input. The data can be passed inline or from standard input.\n" 90 | " Inline and standard input can be mixed. The inline input gets executed before the standard input.\n\n" 91 | 92 | " The patterns of the data input for each command are listed as follows:\n" 93 | " write fields: [= ]*\n" 94 | " write bytes: [[,[,]* ]|[-, ]]*\n" 95 | " clear fields: [ ]*\n" 96 | " clear bytes: [[-] ]*\n\n" 97 | 98 | " When using inline input:\n" 99 | " * Each entry should be separated by a white space.\n" 100 | " * Quote marks are needed if spaces exist in an entry.\n\n" 101 | 102 | " When using standard input:\n" 103 | " * Each entry should be on its own line.\n" 104 | " * Quote marks are not needed if spaces exist in an entry.\n" 105 | " * Comments are supported. Any input from a ';' symbol to the end of the line is ignored.\n\n" 106 | 107 | " Notes for bytes:\n" 108 | " * Offset range is inclusive. Range, non-range and sequence inputs can be mixed.\n" 109 | " * Writing a single byte value to an offset: ,\n" 110 | " * Writing a single byte value to all offsets in range: -,\n" 111 | " * Writing a sequence of bytes starting at an offset: ,,,...\n\n" 112 | 113 | " Notes for fields:\n" 114 | " * The value input should be in the same pattern like in the output of the read command.\n" 115 | ); 116 | 117 | printf("\n" 118 | "USAGE EXAMPLE\n" 119 | " The input for the following command can be passed inline:\n" 120 | " eeprom-util write fields 6 0x20 \"Major Revision=1.20\" \"Production Date=01/Feb/2018\" \\\n" 121 | " \"1st MAC Address=01:23:45:67:89:ab\"\n" 122 | " or via the standard input:\n" 123 | " eeprom-util write fields 6 0x20 < fields_data\n" 124 | " Where fields_data is the name of a file containing 3 non empty lines:\n" 125 | " Major Revision=1.20\n" 126 | " Production Date=01/Feb/2018\n" 127 | " 1st MAC Address=01:23:45:67:89:ab\n" 128 | ); 129 | } 130 | 131 | printf("\n"); 132 | } 133 | 134 | static void message_exit(const char *message) 135 | { 136 | ASSERT(message); 137 | 138 | eprintf(COLOR_RED "%s" COLOR_RESET, message); 139 | print_help(); 140 | exit(1); 141 | } 142 | 143 | static void cond_usage_exit(bool cond, const char *message) 144 | { 145 | if (!cond) 146 | return; 147 | 148 | message_exit(message); 149 | } 150 | 151 | static void usage_exit(void) 152 | { 153 | print_help(); 154 | exit(0); 155 | } 156 | 157 | static enum action parse_action(int argc, char *argv[]) 158 | { 159 | ASSERT(argv && argc > 0); 160 | 161 | if (!strncmp(argv[0], "list", 4)) { 162 | return EEPROM_LIST; 163 | } else if (!strncmp(argv[0], "read", 4)) { 164 | return EEPROM_READ; 165 | } else if (write_enabled() && !strncmp(argv[0], "clear", 5)) { 166 | if (argc > 1 && (!strncmp(argv[1], "fields", 6))) 167 | return EEPROM_CLEAR_FIELDS; 168 | if (argc > 1 && (!strncmp(argv[1], "bytes", 5))) 169 | return EEPROM_CLEAR_BYTES; 170 | 171 | return EEPROM_CLEAR; 172 | } else if (write_enabled() && !strncmp(argv[0], "write", 5)) { 173 | if (argc > 1) { 174 | if (!strncmp(argv[1], "fields", 6)) { 175 | return EEPROM_WRITE_FIELDS; 176 | } else if (!strncmp(argv[1], "bytes", 5)) { 177 | return EEPROM_WRITE_BYTES; 178 | } 179 | } 180 | } else if (!strncmp(argv[0], "help", 4) || 181 | !strncmp(argv[0], "-h", 2) || 182 | !strncmp(argv[0], "--help", 6)) { 183 | usage_exit(); 184 | } else if (!strncmp(argv[0], "version", 7) || 185 | !strncmp(argv[0], "-v", 2) || 186 | !strncmp(argv[0], "--version", 9)) { 187 | print_banner(); 188 | exit(0); 189 | } 190 | 191 | message_exit("Unknown function!\n"); 192 | return EEPROM_ACTION_INVALID; //To appease the compiler 193 | } 194 | 195 | static enum layout_version parse_layout_version(char *str) 196 | { 197 | ASSERT(str); 198 | 199 | if (!strncmp(str, "legacy", 6)) 200 | return LAYOUT_LEGACY; 201 | else if (!strncmp(str, "raw", 3)) 202 | return RAW_DATA; 203 | else if (!strncmp(str, "auto", 4)) 204 | return LAYOUT_AUTODETECT; 205 | else if(!strncmp(str, "v", 1)) 206 | str++; 207 | 208 | int layout = LAYOUT_UNRECOGNIZED; 209 | if (strtoi_base(&str, &layout, 10) != STRTOI_STR_END) 210 | message_exit("Invalid layout version!\n"); 211 | 212 | if (layout < LAYOUT_AUTODETECT || layout >= LAYOUT_UNRECOGNIZED) 213 | message_exit("Unknown layout version!\n"); 214 | 215 | return (enum layout_version)layout; 216 | } 217 | 218 | static enum print_format parse_print_format(char *str) 219 | { 220 | ASSERT(str); 221 | 222 | if (!strncmp(str, "default", 7)) 223 | return FORMAT_DEFAULT; 224 | else if (!strncmp(str, "dump", 4)) 225 | return FORMAT_DUMP; 226 | 227 | message_exit("Unknown print format!\n"); 228 | return FORMAT_DEFAULT; //To appease the compiler 229 | } 230 | 231 | static int parse_i2c_bus(char *str) 232 | { 233 | ASSERT(str); 234 | 235 | int value; 236 | if (strtoi(&str, &value) != STRTOI_STR_END) 237 | message_exit("Invalid bus number!\n"); 238 | 239 | if (value < MIN_I2C_BUS || value > MAX_I2C_BUS) { 240 | ieprintf("Bus '%d' is out of range (%d-%d)", value, 241 | MIN_I2C_BUS, MAX_I2C_BUS); 242 | exit(1); 243 | } 244 | 245 | return value; 246 | } 247 | 248 | static int parse_i2c_addr(char *str) 249 | { 250 | ASSERT(str); 251 | 252 | int value; 253 | if (strtoi(&str, &value) != STRTOI_STR_END) 254 | message_exit("Invalid address number!\n"); 255 | 256 | if (value < MIN_I2C_ADDR || value > MAX_I2C_ADDR) { 257 | ieprintf("Address '0x%02x' is out of range (0x%02x-0x%02x)", 258 | value, MIN_I2C_ADDR, MAX_I2C_ADDR); 259 | exit(1); 260 | } 261 | 262 | return value; 263 | } 264 | 265 | #ifdef ENABLE_WRITE 266 | // The max size of a conventional line from stdin. defined as: 267 | // MAX[ (field name) + (1 for '=') + (field value) + (1 for '/0') ] 268 | #define STDIN_LINE_SIZE 47 269 | 270 | // The max line count from stdin. defines as num of fields in layout v4 271 | #define STDIN_LINES_COUNT 18 272 | 273 | // The size of each reallocation of stdin line size or line count 274 | #define STDIN_REALLOC_SIZE 10 275 | 276 | // Macro for printing input syntax error messages 277 | #define iseprintf(str) ieprintf("Syntax error in \"%s\"", str) 278 | 279 | /* 280 | * mem_realloc - Realloc memory if needed 281 | * 282 | * If memory needed is not smaller than memory available, allocate more memory 283 | * for the same allocation and update the pointer. 284 | * 285 | * @ptr A pointer to the allocated memory 286 | * @mem_needed The current memory size (in blocks) needed for the allocation 287 | * @mem_size A pointer to where the current memory size (in blocks) is saved 288 | * @bytes The size of every block of memory 289 | * 290 | * Returns: 0 on success. -ENOMEM on failure. 291 | */ 292 | static int mem_realloc(void **ptr, unsigned int mem_needed, 293 | unsigned int *mem_size, size_t bytes) 294 | { 295 | ASSERT(ptr && mem_size); 296 | ASSERT(mem_needed > 0 && *mem_size > 0 && bytes > 0); 297 | 298 | if (mem_needed < *mem_size) 299 | return 0; 300 | 301 | unsigned int new_size = *mem_size + STDIN_REALLOC_SIZE; 302 | void *new_alloc = realloc(*ptr, new_size * bytes); 303 | if (!new_alloc) 304 | return -ENOMEM; 305 | 306 | *mem_size = new_size; 307 | *ptr = new_alloc; 308 | return 0; 309 | } 310 | 311 | /* 312 | * read_line_stdin - Read one line from stdin. Ignore empty lines and comments. 313 | * 314 | * @line A pointer to where a string will be allocated and populated 315 | * with the next non empty line from stdin. 316 | * 317 | * Returns: 0 on success. -ENOMEM on failure. 318 | */ 319 | static int read_line_stdin(char **line) 320 | { 321 | ASSERT(line); 322 | 323 | int value = fgetc(stdin); 324 | while (value == '\n') 325 | value = fgetc(stdin); 326 | 327 | if (value == EOF) { 328 | *line = NULL; 329 | return 0; 330 | } 331 | 332 | unsigned int msize = STDIN_LINE_SIZE, pos = 0; 333 | *line = malloc(msize * sizeof(char)); 334 | if (!*line) 335 | return -ENOMEM; 336 | 337 | while (value != EOF && value != '\n' && value != ';') { 338 | (*line)[pos++] = value; 339 | int ret = mem_realloc((void**)line, pos, &msize, sizeof(char)); 340 | if (ret) { 341 | free(*line); 342 | return ret; 343 | } 344 | 345 | value = fgetc(stdin); 346 | } 347 | 348 | // Consume the comment 349 | if (value == ';') 350 | while (value != EOF && value != '\n') 351 | value = fgetc(stdin); 352 | 353 | // Remove white space at the end or before the comment symbol 354 | while (pos > 0 && isblank((*line)[pos - 1])) 355 | pos--; 356 | 357 | // Skip comment only lines or whitespace only lines 358 | if (pos == 0) { 359 | free(*line); 360 | return read_line_stdin(line); 361 | } 362 | 363 | (*line)[pos] = '\0'; 364 | return 0; 365 | } 366 | 367 | /* 368 | * free_stdin - Free allocations made by read_lines_stdin() 369 | * 370 | * @input An allocated array of strings 371 | * @size: The size of the allocated string array 372 | */ 373 | static void free_stdin(char **input, int size) 374 | { 375 | ASSERT(input); 376 | for (int i = 0; i < size; i++) 377 | free(input[i]); 378 | free(input); 379 | } 380 | 381 | /* 382 | * read_lines_stdin - Read non empty lines from stdin. 383 | * 384 | * Allocate an array of strings. populate it with non empty lines from stdin. 385 | * Handle errors by printing an error message to the user, clear the allocated 386 | * memory and propagate the error. 387 | * 388 | * @input A pointer to where an allocated array of strings will be 389 | * written. Each string is a non empty line from stdin. 390 | * @size: A pointer to where the size of the allocated string array 391 | * will be written. 392 | * 393 | * Returns: 0 on success. -ENOMEM on failure. 394 | */ 395 | static int read_lines_stdin(char ***input, int *size) 396 | { 397 | ASSERT(input && size); 398 | 399 | unsigned int msize = STDIN_LINES_COUNT, i = 0; 400 | *input = malloc(msize * sizeof(char *)); 401 | if (!*input) { 402 | perror(STR_ENO_MEM); 403 | return -ENOMEM; 404 | } 405 | 406 | char *line; 407 | int ret = read_line_stdin(&line); 408 | if (ret || !line) 409 | goto cleanup; 410 | 411 | while (line) { 412 | (*input)[i++] = line; 413 | ret = mem_realloc((void**)input, i, &msize, sizeof(char *)); 414 | if (ret) 415 | goto cleanup; 416 | 417 | ret = read_line_stdin(&line); 418 | if (ret) 419 | goto cleanup; 420 | } 421 | 422 | *size = i; 423 | return 0; 424 | 425 | cleanup: 426 | if (ret == -ENOMEM) 427 | perror(STR_ENO_MEM); 428 | free_stdin(*input, i); 429 | *size = 0; 430 | return ret; 431 | } 432 | 433 | /* 434 | * add_lines_from_stdin - Add non empty lines from stdin to a strings array. 435 | * 436 | * Read non empty lines from stdin. Copy the given array. Append lines from 437 | * stream to the copied array. Replace the given array with the copied one. 438 | * 439 | * Note: The function does not free any allocations of the given array. 440 | * 441 | * @input A pointer to a strings array and where to save the result. 442 | * @size: A pointer to the size of the array and where to save the size 443 | * of the result. 444 | * 445 | * Returns: 0 on success. -ENOMEM on failure. 446 | */ 447 | static int add_lines_from_stdin(char ***input, int *size) 448 | { 449 | ASSERT(input && size); 450 | 451 | char **stdin_input; 452 | int stdin_size, i, ret, input_size = *size; 453 | ret = read_lines_stdin(&stdin_input, &stdin_size); 454 | if (ret) 455 | return ret; 456 | 457 | int total_size = input_size + stdin_size; 458 | char **lines = malloc(total_size * sizeof(char *)); 459 | if (!lines) { 460 | free_stdin(stdin_input, stdin_size); 461 | perror(STR_ENO_MEM); 462 | return -ENOMEM; 463 | } 464 | 465 | // Copy command line input 466 | for (i = 0; i < input_size; i++) 467 | lines[i] = (*input)[i]; 468 | 469 | // Copy input lines from stream 470 | for (i = 0; i < stdin_size; i++) 471 | lines[input_size + i] = stdin_input[i]; 472 | 473 | free(stdin_input); 474 | 475 | *input = lines; 476 | *size = total_size; 477 | 478 | return 0; 479 | } 480 | 481 | /* 482 | * parse_bytes_list - parse the strings representing bytes offsets 483 | * 484 | * Allocate a bytes_range array, verify the syntax of the input strings and 485 | * for each array element set the bytes 'start' offset and 'end' offset as 486 | * extracted from the input strings. 487 | * 488 | * @input: A string array containing strings in the expected format: 489 | * "[-]" 490 | * @size: The size of input[] 491 | * @data: A pointer to a data array where to save the result 492 | * 493 | * Returns: 0 on success. -EINVAL or -ENOMEM on failure. 494 | */ 495 | static int parse_bytes_list(char *input[], int size, struct data_array *data) 496 | { 497 | ASSERT(input && *input && data); 498 | ASSERT(size > 0); 499 | 500 | struct bytes_range *bytes_list; 501 | bytes_list = malloc(sizeof(struct bytes_range) * size); 502 | if (!bytes_list) { 503 | perror(STR_ENO_MEM); 504 | return -ENOMEM; 505 | } 506 | 507 | int i; 508 | for (i = 0; i < size ; i++) { 509 | char *str = input[i]; 510 | int ret = strtoi(&str, &bytes_list[i].start); 511 | 512 | if ((ret == STRTOI_STR_CON) && (*str == '-')) { 513 | str++; 514 | if (strtoi(&str, &bytes_list[i].end) != STRTOI_STR_END) 515 | goto syntax_error; 516 | } else if (ret == STRTOI_STR_END) { 517 | bytes_list[i].end = bytes_list[i].start; 518 | } else { 519 | goto syntax_error; 520 | } 521 | } 522 | 523 | data->bytes_list = bytes_list; 524 | data->size = size; 525 | return 0; 526 | 527 | syntax_error: 528 | iseprintf(input[i]); 529 | free(bytes_list); 530 | return -EINVAL; 531 | } 532 | 533 | /* 534 | * parse_bytes_changes - parse the strings representing new bytes values 535 | * 536 | * Count the amount of bytes value changes in the input and allocate a struct 537 | * bytes_change array of the same size. Verify the syntax of the input strings. 538 | * For each array element, set the values of 'start', 'end' and 'value' as 539 | * extracted from the input strings. 540 | * 541 | * @input: A string array containing strings in the expected format: 542 | * "[[[,]+ ]|[-, ]]*" 543 | * @size: The size of input[] 544 | * @data: A pointer to a data array where to save the result 545 | * 546 | * Returns: 0 on success. -EINVAL or -ENOMEM on failure. 547 | */ 548 | static int parse_bytes_changes(char *input[], int size, struct data_array *data) 549 | { 550 | ASSERT(input && *input && data); 551 | ASSERT(size > 0); 552 | 553 | // Count the total amount of bytes value changes. 554 | // It should be equal to the number of ',' symbols in the input. 555 | int i, j, total_changes = 0; 556 | for (i = 0; i < size; i++) 557 | for (j = 0; input[i][j]; j++) 558 | if (input[i][j] == ',') 559 | total_changes++; 560 | 561 | struct bytes_change *changes; 562 | changes = malloc(sizeof(struct bytes_change) * total_changes); 563 | if (!changes) { 564 | perror(STR_ENO_MEM); 565 | return -ENOMEM; 566 | } 567 | 568 | int count_changes = 0; 569 | for (i = 0; i < size; i++) { 570 | char *bytes_input = input[i]; 571 | int start, end, value; 572 | 573 | if (strtoi(&bytes_input, &start) != STRTOI_STR_CON) 574 | goto syntax_error; 575 | 576 | if (*bytes_input == '-') { 577 | bytes_input++; 578 | if (strtoi(&bytes_input, &end) != STRTOI_STR_CON) 579 | goto syntax_error; 580 | } else { 581 | end = start; 582 | } 583 | 584 | if (*bytes_input != ',') 585 | goto syntax_error; 586 | 587 | // Each loop saves a change of one byte value. If offset is 588 | // range (start != end), only one byte change is allowed. 589 | do { 590 | bytes_input++; 591 | if (strtoi(&bytes_input, &value) < 0) 592 | goto syntax_error; 593 | 594 | if (count_changes >= total_changes) 595 | goto syntax_error; 596 | 597 | changes[count_changes].start = start++; 598 | changes[count_changes].end = end++; 599 | changes[count_changes++].value = value; 600 | 601 | } while (*bytes_input == ',' && start == end); 602 | 603 | if (*bytes_input != '\0') 604 | goto syntax_error; 605 | } 606 | 607 | ASSERT(count_changes == total_changes); 608 | 609 | data->bytes_changes = changes; 610 | data->size = count_changes; 611 | return 0; 612 | 613 | syntax_error: 614 | iseprintf(input[i]); 615 | free(changes); 616 | return -EINVAL; 617 | } 618 | 619 | /* 620 | * parse_field_changes - parse the strings representing new fields values 621 | * 622 | * Allocate a field_change array, verify the syntax of the input strings and 623 | * for each array element set the 'field' and 'value' strings as extracted 624 | * from the input strings. 625 | * 626 | * @input: A string array containing strings in the expected format: 627 | * "=" 628 | * @size: The size of input[] 629 | * @data: A pointer to a data array where to save the result 630 | * 631 | * Returns: 0 on success. -EINVAL or -ENOMEM on failure. 632 | */ 633 | static int parse_field_changes(char *input[], int size, struct data_array *data) 634 | { 635 | ASSERT(input && *input && data); 636 | ASSERT(size > 0); 637 | 638 | struct field_change *changes; 639 | changes = malloc(sizeof(struct field_change) * size); 640 | if (!changes) { 641 | perror(STR_ENO_MEM); 642 | return -ENOMEM; 643 | } 644 | 645 | int i; 646 | for (i = 0; i < size; i++) { 647 | char *delim = strchr(input[i], '='); 648 | if (!delim || input[i] == delim) 649 | goto syntax_error; 650 | 651 | *delim = '\0'; 652 | changes[i].field = input[i]; 653 | changes[i].value = delim + 1; 654 | } 655 | 656 | data->fields_changes = changes; 657 | data->size = size; 658 | return 0; 659 | 660 | syntax_error: 661 | iseprintf(input[i]); 662 | free(changes); 663 | return -EINVAL; 664 | } 665 | 666 | #else 667 | static inline int add_lines_from_stdin(char ***input, int *size) 668 | { 669 | return -ENOSYS; 670 | } 671 | 672 | static inline int parse_bytes_list(char *input[], int size, 673 | struct data_array *data) 674 | { 675 | return -ENOSYS; 676 | } 677 | 678 | static inline int parse_bytes_changes(char *input[], int size, 679 | struct data_array *data) 680 | { 681 | return -ENOSYS; 682 | } 683 | 684 | static inline int parse_field_changes(char *input[], int size, 685 | struct data_array *data) 686 | { 687 | return -ENOSYS; 688 | } 689 | #endif 690 | 691 | #define NEXT_PARAM(argc, argv) {(argc)--; (argv)++;} 692 | int main(int argc, char *argv[]) 693 | { 694 | struct command *cmd; 695 | enum action action = EEPROM_ACTION_INVALID; 696 | struct options options = { 697 | .layout_ver = LAYOUT_AUTODETECT, 698 | .print_format = FORMAT_DEFAULT, 699 | }; 700 | struct data_array data; 701 | int ret = -1, parse_ret = 0, input_size = 0; 702 | char **input = NULL; 703 | bool is_stdin = !isatty(STDIN_FILENO); 704 | errno = 0; 705 | 706 | if (argc <= 1) 707 | usage_exit(); 708 | 709 | NEXT_PARAM(argc, argv); // Skip program name 710 | action = parse_action(argc, argv); 711 | NEXT_PARAM(argc, argv); 712 | if (action == EEPROM_LIST && argc == 0) 713 | goto done; 714 | 715 | // parse_action already took care of parsing the bytes/fields qualifier 716 | if (action == EEPROM_WRITE_BYTES || action == EEPROM_WRITE_FIELDS || 717 | action == EEPROM_CLEAR_FIELDS || action == EEPROM_CLEAR_BYTES) 718 | NEXT_PARAM(argc, argv); 719 | 720 | // The "all" qualifier is optional for clear command 721 | if (action == EEPROM_CLEAR && argc > 0 && !strncmp(argv[0], "all", 3)) 722 | NEXT_PARAM(argc, argv); 723 | 724 | // parse optional parameters 725 | while (argc > 0 && argv[0][0] == '-') { 726 | switch (argv[0][1]) { 727 | case 'l': 728 | NEXT_PARAM(argc, argv); 729 | cond_usage_exit(argc < 1, "Missing layout version!\n"); 730 | options.layout_ver = parse_layout_version(argv[0]); 731 | break; 732 | case 'f': 733 | NEXT_PARAM(argc, argv); 734 | cond_usage_exit(argc < 1, "Missing print format!\n"); 735 | options.print_format = parse_print_format(argv[0]);; 736 | break; 737 | default: 738 | message_exit("Invalid option parameter!\n"); 739 | } 740 | 741 | NEXT_PARAM(argc, argv); 742 | } 743 | 744 | cond_usage_exit(argc < 1, "Missing I2C bus & address parameters!\n"); 745 | options.i2c_bus = parse_i2c_bus(argv[0]); 746 | NEXT_PARAM(argc, argv); 747 | 748 | if (action == EEPROM_LIST) 749 | goto done; 750 | 751 | cond_usage_exit(argc < 1, "Missing I2C address parameter!\n"); 752 | options.i2c_addr = parse_i2c_addr(argv[0]); 753 | NEXT_PARAM(argc, argv); 754 | 755 | if (action == EEPROM_READ || action == EEPROM_CLEAR) 756 | goto done; 757 | 758 | input = argv; 759 | input_size = argc; 760 | if (is_stdin && add_lines_from_stdin(&input, &input_size)) 761 | return 1; 762 | 763 | cond_usage_exit(input_size == 0, "Missing data input!\n"); 764 | 765 | if (action == EEPROM_WRITE_FIELDS) { 766 | parse_ret = parse_field_changes(input, input_size, &data); 767 | } else if (action == EEPROM_WRITE_BYTES) { 768 | parse_ret = parse_bytes_changes(input, input_size, &data); 769 | } else if (action == EEPROM_CLEAR_FIELDS) { 770 | data.fields_list = input; 771 | data.size = input_size; 772 | } else if (action == EEPROM_CLEAR_BYTES) { 773 | parse_ret = parse_bytes_list(input, input_size, &data); 774 | } 775 | 776 | if (parse_ret) 777 | goto clean_input; 778 | 779 | done: 780 | cmd = new_command(action, &options, &data); 781 | if (!cmd) 782 | perror(STR_ENO_MEM); 783 | else 784 | ret = cmd->execute(cmd); 785 | 786 | free_command(cmd); 787 | 788 | if (action == EEPROM_WRITE_FIELDS) 789 | free(data.fields_changes); 790 | else if (action == EEPROM_WRITE_BYTES) 791 | free(data.bytes_changes); 792 | else if (action == EEPROM_CLEAR_BYTES) 793 | free(data.bytes_list); 794 | 795 | clean_input: 796 | if (input && is_stdin) { 797 | // Free input data from stdin. Don't free command line input 798 | for (int i = argc; i < input_size; i++) 799 | free(input[i]); 800 | free(input); 801 | } 802 | 803 | return ret ? 1 : 0; 804 | } 805 | --------------------------------------------------------------------------------