First commit after revamping bqtool and adding
authorAneesh V <aneesh@ti.com>
Fri, 9 Oct 2015 13:23:45 +0000 (06:23 -0700)
committerAneesh V <aneesh@ti.com>
Fri, 9 Oct 2015 13:23:45 +0000 (06:23 -0700)
new commands

Signed-off-by: Aneesh V <aneesh@ti.com>
Android.mk [changed mode: 0755->0644]
README
bqTool_1.0_manifest.html [changed mode: 0755->0644]
bqt.h [new file with mode: 0644]
bqtool.c [deleted file]
expression-parser.c [new file with mode: 0644]
gauge-simulator.c [new file with mode: 0644]
gauge.c [new file with mode: 0644]
interface-linux-i2c.c [new file with mode: 0644]
main.c [new file with mode: 0644]

old mode 100755 (executable)
new mode 100644 (file)
index 3b3a3d2..6d6f33d
@@ -4,9 +4,11 @@ LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES:= bqtool.c
+LOCAL_SRC_FILES:= main.c expression-parser.c gauge.c interface-linux-i2c.c gauge-simulator.c
+LOCAL_CFLAGS += -Wno-error=sign-compare
+#LOCAL_LDFLAGS := --gc-sections
 LOCAL_MODULE := bqtool
-LOCAL_STATIC_LIBRARIES := libcutils libc
+LOCAL_STATIC_LIBRARIES := libcutils libc libm
 include $(BUILD_EXECUTABLE)
 
 endif  # TARGET_SIMULATOR != true
diff --git a/README b/README
index a10992197f59840a1e6c70c303abb12ed1775f48..4bf5cb0ec5488dc53dedd5f63901a3ffc4c33ac6 100644 (file)
--- a/README
+++ b/README
@@ -7,12 +7,14 @@ Android utility for programming and debugging TI's bq27xxx family of gas gauges
 
 Iniitial build
 ==============
+cd <android-root-dir>
 make TARGET_PRODUCT=<product> bqtool
 
 Incremental build
 =================
 cd <android-root-dir>
 source build/envsetup.sh
+cd <bqtool-dir>
 mm TARGET_PRODUCT=<product>
 
 *** Potential Build Issue ***
@@ -24,9 +26,17 @@ directory or provide an alternate include path to the compiler.
 
 
 Commands supported:
---flash :
-       bqtool --flash <bqfs file|dffs file>
-       Eg: bqtool --flash /system/bin/bq27520_v302.bqfs
+--import-gg-csv                --params-csv=<gg-csv> [--unseal-key=<key> --fullaccess-key=<key> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num> --reset --exit-seal=<seal-state>]
+
+--rom-gauge-monitor    --init-csv=<gg-csv> --save-restore-csv=<gg-csv> --period=<prd-ms> [--timestamp --unseal-key=<key> --fullaccess-key=<key> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num> --exit-seal=<seal-state>]
+
+--bqfs-flash           --bqfs-flash=<bqfs/dffs file> [--mode=<interface> --max-block-len=<max-blk-len> --unseal-key=<key> --fullaccess-key=<key> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num> --reset --exit-seal=<seal-state>]
+
+--export-gg-csv        --params-csv=<gg-csv> [--output-csv=<csv-file> --format-csv=<csv-file> --period=<prd-ms> --timestamp --no-header --unseal-key=<key> --fullaccess-key=<key> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num> --exit-seal=<seal-state>]
+
+--export-regs          --regs-csv=<regs-csv> [--output-csv=<csv-file> --regs-format-csv=<regs-csv> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num>]
+
+--combined-export      --dm-csv=<gg-csv> --regs-csv=<regs-csv> [--output-csv=<csv-file> --dm-format-csv==<csv-rile> --regs-format-csv=<regs-csv> --period=<prd-ms> --timestamp --no-header --unseal-key=<key> --fullaccess-key=<key> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num> --exit-seal=<seal-state>]
 
-       For details of flashstream file format(.bqfs/.dffs) see:
-       http://www.ti.com.cn/cn/lit/an/slua541a/slua541a.pdf
+Note:
+Please see http://www.ti.com.cn/cn/lit/an/slua541a/slua541a.pdf for details of flashstream format used in command --bqfs-flash
old mode 100755 (executable)
new mode 100644 (file)
diff --git a/bqt.h b/bqt.h
new file mode 100644 (file)
index 0000000..07dd979
--- /dev/null
+++ b/bqt.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Inc
+ *
+ * Aneesh V <aneesh@ti.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __BQT_COMMON_H_
+#define __BQT_COMMON_H_
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+/********************************************************
+ * Compile-time feature selection
+ *******************************************************/
+/* Enable this flag for debug prints */
+//#define BQTOOL_DEBUG
+
+/*
+ * Commands supported. Remove unused commands
+ * if necessary to save memory
+ */
+#define CMD_IMPORT_GG_CSV
+#define CMD_EXPORT_GG_CSV
+#define CMD_EXPORT_REGS
+#define CMD_COMBINED_EXPORT
+#define CMD_BQFS_FLASH
+#define CMD_ROM_GAUGE_MONITOR
+
+/*
+ * Gauge simulator for testing the front-end on a PC
+ * using a file to store DM content.Only limited set
+ * of features supported
+ */
+//#define GAUGE_SIMULATOR
+
+#ifndef GAUGE_SIMULATOR
+
+/*
+ * COMM Interface:
+ * Select only one of the below
+ */
+#define GAUGE_COMM_IF_LINUX_I2C
+/* #define GAUGE_COMM_IF_LINUX_HDQ - not supported, just for example */
+
+/*
+ * Gauge families supported:
+ * Remove those you do not want if you want to save memory.
+ * But make sure to leave at least one enabled.
+ */
+#define BQ8032
+#define BQ8034
+#define BQ8035
+#define BQ8036
+#define BQ8037
+#define BQ8101
+
+#endif /* #ifndef GAUGE_SIMULATOR */
+
+/********************************************************
+ * utily macros for message logging
+ *******************************************************/
+void print(FILE *out, const char *fmt, ...);
+#ifdef BQTOOL_DEBUG
+#define pr_dbg(...)    { print(stderr, "bqtool: %s(): ", __func__); print(stderr, __VA_ARGS__); }
+#define pr_dbg_raw(...) {print(stderr, __VA_ARGS__);}
+#else
+#define pr_dbg(...)
+#define pr_dbg_raw(...)
+#endif
+#define pr_info(...)   { print(stdout, "bqtool: %s(): ", __func__); print(stdout, __VA_ARGS__); }
+#define pr_err(...)    { print(stderr, "bqtool: %s(): ", __func__); print(stderr, __VA_ARGS__); }
+#define pr_err_raw(...)        { print(stderr, __VA_ARGS__); }
+
+
+/********************************************************
+ * Exported data structures and functions from main.c
+ *******************************************************/
+/*
+ * Buffer allocated while reading in a line. To be on safer side,
+ * we will re-allocate with an additional MAX_LINE_LEN_GG_CSV
+ * bytes when we run out of buffer, so the real maximum line
+ * length is 2 * MAX_LINE_LEN_GG_CSV
+ */
+#define MAX_LINE_LEN_GG_CSV    300
+#define MAX_CSV_FIELDS         30
+#define MAX_USED_FLD_LEN       100
+#define MAX_NUM_PARAMS         500
+#define MAX_NUM_FIELDS         30
+#define BQFS_MAX_DATA_BLOCK_LEN        (96 + 4)
+
+struct file_info_t {
+       const char *fname;
+       FILE    *fd;
+       int     line_num;
+       char    *line_buf;
+       int     max_line_len;
+};
+
+enum field_t {
+       USED_FLD_NAME = 0,
+       USED_FLD_VALUE,
+       USED_FLD_MIN_VAL,
+       USED_FLD_MAX_VAL,
+       USED_FLD_DATATYPE,
+       USED_FLD_DATA_LENGTH,
+       USED_FLD_ADDRESS_OFFST,
+       USED_FLD_DISPLAY_FORMAT,
+       USED_FLD_READ_FORMULA,
+       USED_FLD_WRITE_FORMULA,
+       NUM_USED_FIELDS, /* Always should be the last entry */
+};
+
+struct csv_header_t {
+       uint16_t                device_num;
+       uint16_t                fw_version;
+       uint8_t                 fw_ver_bld;
+       int                     num_fields;
+       struct file_info_t      *file;
+       char                    **field_names;
+};
+
+enum val_type_t {
+       DATATYPE_I = 0,
+       DATATYPE_U,
+       DATATYPE_F,
+       DATATYPE_S,
+};
+
+enum param_csv_format_t {
+       DISP_FMT_I,
+       DISP_FMT_H,
+       DISP_FMT_F,
+       DISP_FMT_S,
+};
+
+union val_t {
+       int32_t         i;
+       uint32_t        u;
+       float           f;
+};
+
+enum endianness_t {
+       BYTEORDER_LE = 1,
+       BYTEORDER_BE
+};
+
+enum gauge_ic_type {
+       GAUGE_IC_X = 1,
+       GAUGE_IC_B
+};
+
+enum gauge_op_t {
+       OP_DM_READ = 1,
+       OP_DM_WRITE,
+       OP_BQFS_FLASH,
+       OP_REG_DUMP
+};
+
+struct csv_info_t;
+struct gauge_info_t {
+       uint16_t        device_num;
+       uint16_t        fw_version;
+       uint8_t         fw_ver_bld;
+       uint32_t        family;
+       uint8_t         slave_addr;
+       void            *interface;
+       uint8_t         orig_seal_status;
+       uint8_t         endianness;
+       uint32_t        unseal_key;
+       uint32_t        fullacccess_key;
+       uint8_t         unseal_key_found;
+       uint8_t         fullaccess_key_found;
+       uint8_t         ic;
+       uint8_t         op;
+       int (*read_params)(struct csv_info_t *csv, struct gauge_info_t *gauge);
+       int (*write_params)(struct csv_info_t *csv, struct gauge_info_t *gauge);
+       int (*open_dm)(struct gauge_info_t *gauge, int argc, char **argv);
+       void (*close_dm)(struct gauge_info_t *gauge, int argc, char **argv);
+       void (*reset)(struct gauge_info_t *gauge);
+       int (*itpor)(struct gauge_info_t *gauge);
+       /* Communication interface - OS depedent */
+       int (*read)(struct gauge_info_t *gauge, uint8_t slave_addr, uint8_t *buf, uint8_t len);
+       int (*write)(struct gauge_info_t *gauge, uint8_t slave_addr, uint8_t *buf, uint8_t len);
+       /* OS communication support */
+       int (*lock_gauge_interface)(struct gauge_info_t *gauge);
+       int (*unlock_gauge_interface)(struct gauge_info_t *gauge);
+       int (*init_comm_interface)(struct gauge_info_t *gauge, int argc, char **argv);
+       void (*close_comm_interface)(struct gauge_info_t *gauge);
+       void (*sleep_ms)(uint16_t ms);
+};
+
+struct param_t {
+       uint8_t raw_type;
+       uint8_t disp_type;
+       uint8_t csv_fraction_len;
+       char    *val_s; /* for parameters of datatype 's' */
+       const char *fmt_str;
+       char *name;
+       uint32_t offset;
+       union val_t val;
+       union val_t min;
+       union val_t max;
+       struct queue_t *read_formula_expr;
+       struct queue_t *write_formula_expr;
+       uint8_t data_len;
+       char    **fields;
+};
+
+struct csv_info_t {
+       struct csv_header_t     *header;
+       uint8_t                 used_field_index[NUM_USED_FIELDS];
+       uint32_t                unseal_key;
+       uint32_t                fullaccess_key;
+       uint8_t                 unseal_key_found;
+       uint8_t                 fullaccess_key_found;
+       uint8_t                 value_fld_ind;
+       struct param_t          *params;
+       uint32_t                num_params;
+};
+
+char *read_line(struct file_info_t *file);
+void free_file(struct file_info_t *file);
+struct file_info_t *create_file(const char *fname, const char *mode,
+       int max_line_len);
+int extract_int(const char *str, int base, long long int *res);
+const char *get_cmdline_argument(int argc, char **argv,
+       const char *arg_identifier, int arg_with_val);
+int update_value_string(struct param_t *param, int value_fld_ind);
+int check_limits(struct param_t *param);
+
+
+/********************************************************
+ * Exported data structures and functions from expression-parser.c
+ *******************************************************/
+struct node_t {
+       struct node_t *next;
+       struct node_t *prev;
+       void *val_p;
+};
+
+struct queue_t {
+       struct node_t *head;
+       struct node_t *tail;
+       struct node_t *curr;
+};
+
+void delete_queue(struct queue_t *q);
+struct queue_t *parse_expression(const char *expr);
+double evaluate_expr(struct queue_t *rpn_q, double val);
+
+/********************************************************
+ * Exported data structures and functions from gauge.c
+ *******************************************************/
+/* Registers common */
+#define REG_CONTROL            0x00
+#define REG_BLOCK_DATA_CLASS   0x3E
+#define REG_DATA_BLOCK         0x3F
+#define REG_BLOCK_DATA         0x40
+#define REG_BLOCK_DATA_CHECKSUM        0x60
+#define REG_BLOCK_DATA_CONTROL 0x61
+
+/* CONTROL commands */
+#define CTRL_CMD_STATUS                0x0000
+#define CTRL_CMD_DEVICE_TYPE   0x0001
+#define CTRL_CMD_FW_VERSION    0x0002
+#define CTRL_CMD_SEAL          0x0020
+#define CTRL_CMD_RESET         0x0041
+
+/* Register bit fields */
+#define CTRL_STATUS_SS (1 << 13)
+#define CTRL_STATUS_FAS        (1 << 14)
+
+/* ROM gauge registers */
+#define ROM_GAUGE_FLAGS                                0x06
+#define CTRL_CMD_ROM_GAUGE_SOFT_RESET          0x0042
+#define CTRL_CMD_ROM_GAUGE_EXIT_CFGUPDATE      0x0043
+#define CTRL_CMD_ROM_GAUGE_SET_CFGUPDATE       0x0013
+#define CTRL_CMD_ROM_GAUGE_DM_CODE             0x0004
+
+
+/* ROM gauge bit fields */
+#define ROM_GAUGE_FLAG_ITPOR           0x20
+#define ROM_GAUGE_FLAG_CFGUPDATE_MODE  0x10
+#define ROM_GAUGE_CTRL_STATUS_INITCOMP 0x80
+
+/* ROM gauge unseal key */
+#define ROM_GAUGE_UNSEAL_KEY           0x80008000
+
+
+int init_gauge_interface(struct gauge_info_t *gauge, int argc, char **argv);
+int do_read(struct gauge_info_t *gauge, uint8_t *buf, uint8_t len);
+int do_write(struct gauge_info_t *gauge, uint8_t *buf, uint8_t len);
+int read_regs(struct csv_info_t *csv, struct gauge_info_t *gauge);
+
+/********************************************************
+ * OS/Communication Interface Abstraction
+ *******************************************************/
+int setup_comm_callbacks(struct gauge_info_t *gauge);
+#endif
diff --git a/bqtool.c b/bqtool.c
deleted file mode 100644 (file)
index 6d13d7c..0000000
--- a/bqtool.c
+++ /dev/null
@@ -1,607 +0,0 @@
-/*
- * Copyright (C) 2014 Texas Instruments Inc
- *
- * Aneesh V <aneesh@ti.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Android utility for programming and debugging TI's bq27xxx
- * family of gas gauges
- *
- * Commands supported:
- * --flash :
- *     bqtool --flash <bqfs file|dffs file>
- *     Eg: bqtool --flash /system/bin/bq27520_v302.bqfs
- *
- *     For details of flashstream file format(.bqfs/.dffs) see:
- *     http://www.ti.com.cn/cn/lit/an/slua541a/slua541a.pdf
- *
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <linux/i2c.h>
-#include <linux/i2c-dev.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-/* #define BQPROG_DEBUG */
-
-#define CMD_MAX_DATA_SIZE      96
-#define MAX_LINE_LEN           ((CMD_MAX_DATA_SIZE + 4) * 3)
-#define RETRY_LIMIT            3
-#define CMD_RETRY_DELAY                100 /* in ms */
-#define DEFAULT_POLL_INTVL     360
-#define I2C_BUS                        "2"
-#define I2C_DEV                        "/dev/i2c-"I2C_BUS
-#define SYSFS_POLL_INTVL "/sys/module/bq27x00_battery/parameters/poll_interval"
-
-#ifdef __GNUC__
-#define __PACKED       __attribute__((packed))
-#else
-#error "Make sure structure cmd_t is packed"
-#endif
-
-typedef enum {
-       CMD_INVALID = 0,
-       CMD_R,  /* Read */
-       CMD_W,  /* Write */
-       CMD_C,  /* Compare */
-       CMD_X,  /* Delay */
-} cmd_type_t;
-
-/*
- * DO NOT change the order of fields - particularly reg
- * should be immediately followed by data
- */
-typedef struct {
-       cmd_type_t cmd_type;
-       uint8_t addr;
-       uint8_t reg;
-       union {
-               uint8_t bytes[CMD_MAX_DATA_SIZE + 1];
-               uint16_t delay;
-       } data;
-       uint8_t data_len;
-       uint32_t line_num;
-       char    *line;
-} __PACKED cmd_t;
-
-static uint32_t line_num;
-
-/*
- * Print usage of all commands when cmd == NULL
- * Otherwise print the usage of the respective command
- * Currently we have only one commnd
- */
-static void usage(const char *cmd)
-{
-       fprintf(stderr, "Usage: bqtool --flash <bqfs-file|dffs-file>\n");
-}
-
-static void print_delay(cmd_t *cmd)
-{
-       fprintf(stdout, "delay: %d", cmd->data.delay);
-}
-
-static void print_buf(FILE *fd, uint8_t *buf, uint8_t len)
-{
-       uint8_t i;
-
-       for (i = 0; i < len; i++)
-               fprintf(fd, " %02x", buf[i]);
-}
-
-static void print_data(cmd_t *cmd)
-{
-       int i;
-
-       fprintf(stdout, "data: ");
-       print_buf(stdout, cmd->data.bytes, cmd->data_len);
-}
-static void print_addr_reg(cmd_t *cmd)
-{
-       fprintf(stdout, "addr: %02x ", cmd->addr);
-       fprintf(stdout, "reg: %02x ", cmd->reg);
-}
-
-#ifdef BQPROG_DEBUG
-static void print_cmd(cmd_t *cmd)
-{
-       switch (cmd->cmd_type) {
-       case CMD_R:
-               fprintf(stdout, "R: ");
-               print_addr_reg(cmd);
-               break;
-       case CMD_W:
-               fprintf(stdout, "W: ");
-               print_addr_reg(cmd);
-               print_data(cmd);
-               break;
-       case CMD_C:
-               fprintf(stdout, "C: ");
-               print_addr_reg(cmd);
-               print_data(cmd);
-               break;
-       case CMD_X:
-               fprintf(stdout, "X: ");
-               print_delay(cmd);
-               break;
-       default:
-               fprintf(stdout, "Unknown: ");
-               break;
-       }
-       fprintf(stdout, "\n");
-}
-#else
-static void print_cmd(cmd_t *cmd)
-{
-}
-#endif
-
-static int read_bq_poll_intvl(void)
-{
-       int poll_file = -1;
-       int poll_intvl = -1;
-       char buf[20];
-
-       poll_file = open(SYSFS_POLL_INTVL, O_RDONLY);
-
-       if ((poll_file >= 0) && read(poll_file, buf, 20))
-               sscanf(buf, "%d", &poll_intvl);
-       else
-               fprintf(stderr, "Failed to read %s\n", SYSFS_POLL_INTVL);
-
-       if (poll_file >= 0)
-               close(poll_file);
-
-       return poll_intvl;
-}
-
-static bool write_bq_poll_intvl(int poll_intvl)
-{
-       int poll_file = -1;
-       char buf[20];
-
-       poll_file = open(SYSFS_POLL_INTVL, O_RDWR);
-
-       if (poll_file >= 0) {
-               sprintf(buf, "%d", poll_intvl);
-               write(poll_file, buf, 20);
-               close(poll_file);
-       }
-
-       if (poll_intvl == read_bq_poll_intvl())
-               return true;
-       else
-               return false;
-}
-
-static bool i2c_rw(int i2c_file, cmd_t *cmd, int write)
-{
-       int ret;
-       struct i2c_rdwr_ioctl_data i2c_data;
-       char *op;
-       /* msg[0] for write command and msg[1] for read command */
-       struct i2c_msg msgs[2];
-
-       /* Linux expects 7 bit address */
-       msgs[0].addr = cmd->addr >> 1;
-       /* reg is data too as far as I2C xfr is concerned */
-       msgs[0].buf = (uint8_t *)&cmd->reg;
-       msgs[0].flags = 0;
-
-       if (write) {
-               msgs[0].len = cmd->data_len + 1;
-               i2c_data.nmsgs = 1;
-               op = "write";
-       } else {
-               msgs[0].len = 1;
-
-               /* read command */
-               msgs[1].addr = cmd->addr >> 1;
-               msgs[1].buf = (uint8_t *)cmd->data.bytes;
-               msgs[1].flags = I2C_M_RD;
-               msgs[1].len = cmd->data_len;
-
-               i2c_data.nmsgs = 2;
-               op = "read";
-       }
-
-       i2c_data.msgs = msgs;
-       ret = ioctl(i2c_file, I2C_RDWR, &i2c_data);
-       if (ret < 0) {
-               fprintf(stderr, "I2C %s failed at line %d error = %d\n",
-                       op, cmd->line_num, ret);
-               return false;
-       }
-
-       return true;
-}
-
-static bool do_exec_cmd(int i2c_file, cmd_t *cmd)
-{
-       uint8_t tmp_buf[CMD_MAX_DATA_SIZE];
-
-       switch (cmd->cmd_type) {
-       case CMD_R:
-               return i2c_rw(i2c_file, cmd, 0);
-
-       case CMD_W:
-               return i2c_rw(i2c_file, cmd, 1);
-
-       case CMD_C:
-               memcpy(tmp_buf, cmd->data.bytes, cmd->data_len);
-               if (!i2c_rw(i2c_file, cmd, 0))
-                       return false;
-               if (memcmp(tmp_buf, cmd->data.bytes, cmd->data_len)) {
-                       fprintf(stderr, "\nCommand C failed at line %d:\n",
-                               cmd->line_num);
-                       fprintf(stderr, "Expected data:");
-                       print_buf(stderr, tmp_buf, cmd->data_len);
-                       fprintf(stderr, "\nReceived data:");
-                       print_buf(stderr, cmd->data.bytes, cmd->data_len);
-                       fprintf(stderr, "\n");
-                       return false;
-               }
-               return true;
-
-       case CMD_X:
-               usleep(cmd->data.delay * 1000);
-               return true;
-
-       default:
-               fprintf(stderr, "Unsupported command at line %d\n",
-                       cmd->line_num);
-               return false;
-       }
-}
-
-static bool execute_cmd(int i2c_file, cmd_t *cmd)
-{
-       int i = 1;
-       bool ret;
-
-       ret = do_exec_cmd(i2c_file, cmd);
-
-       while (!ret && i < RETRY_LIMIT) {
-               usleep(CMD_RETRY_DELAY * 1000);
-               ret = do_exec_cmd(i2c_file, cmd);
-               i++;
-       }
-
-       if (!ret) {
-               fprintf(stderr, "Command execution failed at line %d"
-                       " addr - 0x%02x reg - 0x%02x, tried %d times\n",
-                       cmd->line_num, cmd->addr, cmd->reg, RETRY_LIMIT);
-       }
-
-       return ret;
-}
-
-static bool get_delay(uint16_t *delay)
-{
-       char *tok;
-       uint32_t temp;
-
-       tok = strtok(NULL, " ");
-       if (!tok)
-               return false; /*end of line or file */
-
-       if (1 != sscanf(tok, "%u", &temp)) {
-               fprintf(stderr, "Syntax error while parsing delay at line %d\n",
-                       line_num);
-               return false; /* syntax error */
-       }
-
-       if (temp > UINT16_MAX) {
-               fprintf(stderr, "Command X delay too high at line %d - %dms\n",
-                       line_num, temp);
-               return false;
-       }
-
-       *delay = (uint16_t)temp;
-       return true;
-
-}
-
-/*
- * Returns:
- *      0: success
- *      1: EOF
- *     -1: Parse error
- */
-static int get_byte(uint8_t *byte)
-{
-       char *tok;
-       unsigned int temp;
-
-       tok = strtok(NULL, " \t\r\n");
-       if (!tok)
-               return 1; /*end of line or file */
-
-       if ((strlen(tok) != 2) || (sscanf(tok, "%2x", &temp) != 1)) {
-                       fprintf(stderr, "Syntax error at line %d, token - %s,"
-                               " temp - %x\n", line_num, tok, temp);
-                       return -1; /* syntax error */
-       }
-
-       *byte = (uint8_t)temp;
-
-       return 0;       /* success */
-}
-
-static bool get_addr_n_reg(cmd_t *cmd)
-{
-       if (get_byte(&cmd->addr))
-               return false;
-
-       if (get_byte(&cmd->reg))
-               return false;
-
-       return true;
-}
-
-static bool get_data_bytes(cmd_t *cmd)
-{
-       int ret, i = 0;
-       cmd->data_len = 0;
-
-       do {
-               ret = get_byte(&cmd->data.bytes[i++]);
-               if (ret == -1)
-                       return false;
-       } while ((ret == 0) && (i <= CMD_MAX_DATA_SIZE));
-
-       if (ret == 0) {
-               fprintf(stderr, "More than allowed number of data bytes at"
-                       " line %d, data_len %d, i %d\n", cmd->line_num,
-                       cmd->data_len, i);
-               return false;
-       }
-
-       cmd->data_len = i - 1;
-
-       return true;
-}
-
-static bool get_line(FILE *bqfs_file, char **buffer)
-{
-       int c;
-       int i = 0;
-       bool ret = true;
-       char *buf;
-
-       buf = malloc(MAX_LINE_LEN);
-       line_num++;
-
-       while (1) {
-               c = fgetc(bqfs_file);
-
-               if (feof(bqfs_file)) {
-#ifdef BQPROG_DEBUG
-                       fprintf(stdout, "EOF\n");
-#endif
-                       break;
-               } else if (ferror(bqfs_file)) {
-                       fprintf(stderr, "File read error\n");
-                       ret = false;
-                       break;
-               }
-
-               if (((c == '\r') || (c == '\n') || (c == '\t')
-                       || (c == ' ')) && (i == 0)) {
-                       /*
-                        * Skip leading white space, if any, at the beginning
-                        * of the line because this interferes with strtok
-                        */
-                       fprintf(stderr, "Leading whitespace at line %d\n",
-                               line_num);
-                       if (c == '\n')
-                               line_num++;
-                       continue;       /* blank line, let's continue */
-               } else if (c == '\n') {
-                       /* We've reached end of line */
-                       break;
-               }
-
-               buf[i++] = c;
-
-               if (i == MAX_LINE_LEN) {
-                       /*
-                        * Re-allocate in case the line is longer than
-                        * expected
-                        */
-                       buf = realloc(buf, MAX_LINE_LEN * 2);
-                       fprintf(stderr, "Line %d longer than expected,"
-                               " reallocating..\n", line_num);
-               } else if (i == MAX_LINE_LEN * 2) {
-                       /*
-                        * The line is already twice the expected maximum length
-                        * - maybe the bqfs/dffs needs to be fixed
-                        */
-                       fprintf(stderr, "Line %d too long, abort parsing..\n",
-                               line_num);
-                       ret = false;
-                       break;
-               }
-       }
-
-       *buffer = buf;
-       buf[i] = '\0';
-
-       if (i < 1)
-               ret = false;
-
-       return ret;
-}
-
-static bool get_cmd(FILE *bqfs_file, cmd_t *cmd)
-{
-       char *res;
-       char *tok;
-       char *buf = NULL;
-       bool ret;
-
-       while ((ret = get_line(bqfs_file, &buf))) {
-               if (buf[0] == ';') {
-                       /*
-                        * Comment line - ignore it and get the
-                        * next line
-                        */
-                       fprintf(stdout, "%s", buf);
-                       free(buf);
-               } else {
-                       break;
-               }
-       }
-
-       if (!ret)
-               goto error;
-
-       cmd->line_num = line_num;
-       tok = strtok(buf, ":");
-       if (!tok || (strlen(tok) != 1)) {
-               fprintf(stderr, "Error parsing command at line %d tok=%s"
-                       " buf=%s", line_num, tok, buf);
-               goto error;
-       }
-
-       switch (tok[0]) {
-       case 'R':
-       case 'r':
-               cmd->cmd_type = CMD_R;
-               if (!get_addr_n_reg(cmd))
-                       goto error;
-               break;
-       case 'W':
-       case 'w':
-               cmd->cmd_type = CMD_W;
-               if (!get_addr_n_reg(cmd))
-                       goto error;
-               if (!get_data_bytes(cmd))
-                       goto error;
-               break;
-       case 'C':
-       case 'c':
-               cmd->cmd_type = CMD_C;
-               if (!get_addr_n_reg(cmd))
-                       goto error;
-               if (!get_data_bytes(cmd))
-                       goto error;
-               break;
-       case 'X':
-       case 'x':
-               cmd->cmd_type = CMD_X;
-               if (!get_delay(&cmd->data.delay))
-                       goto error;
-               break;
-       default:
-               fprintf(stderr, "No command or unexpected command at"
-                       " line %d tok=\"%s\" buf=\"%s\"",
-                       line_num, tok, buf);
-               goto error;
-       }
-
-       print_cmd(cmd);
-       free(buf);
-       return true;
-
-error:
-       cmd->cmd_type = CMD_INVALID;
-       free(buf);
-       return false;
-}
-
-int bqfs_flash(char *fname)
-{
-       int poll_intvl = -1;
-       int i2c_file = -1;
-       int poll_file = -1;
-       FILE *bqfs_file = NULL;
-       char *line = NULL;
-       cmd_t *cmd = NULL;
-       int ret = 0;
-       char buf[20];
-
-       bqfs_file = fopen(fname, "r");
-       if (!bqfs_file) {
-               usage("--flash");
-               ret = -1;
-               goto end;
-       }
-
-       poll_intvl = read_bq_poll_intvl();
-       /* Turn off polling */
-       if (!write_bq_poll_intvl(0)) {
-               fprintf(stderr, "Failed to stop driver polling\n");
-               ret = -1;
-               goto end;
-       }
-
-       i2c_file = open(I2C_DEV, O_RDWR);
-       if (i2c_file < 0) {
-               fprintf(stderr, "Failed to open I2C device %s\n", I2C_DEV);
-               ret = -1;
-               goto end;
-       }
-
-       cmd = malloc(sizeof(cmd_t));
-
-       while (get_cmd(bqfs_file, cmd) && execute_cmd(i2c_file, cmd))
-               fputc('.', stdout);
-
-       if (feof(bqfs_file)) {
-               fprintf(stdout, "\n%s programmed successfully!\n", fname);
-               ret = 0;
-       } else {
-               fprintf(stdout, "\nprogramming %s failed!!\n", fname);
-               ret = -1;
-       }
-
-end:
-       if (poll_intvl >= 0) {
-               if (!write_bq_poll_intvl(poll_intvl))
-                       fprintf(stderr, "Failed to restore driver polling\n");
-       }
-
-       if (poll_file >= 0)
-               close(poll_file);
-
-       if (i2c_file >= 0)
-               close(i2c_file);
-
-       if (bqfs_file)
-               fclose(bqfs_file);
-
-       if (cmd)
-               free(cmd);
-
-       return ret;
-}
-
-int main(int argc, char **argv)
-{
-       if ((argc >= 3) && (strncmp(argv[1], "--flash", 7) == 0)) {
-               return bqfs_flash(argv[2]);
-       } else {
-               usage(argv[0]);
-               return -1;
-       }
-}
diff --git a/expression-parser.c b/expression-parser.c
new file mode 100644 (file)
index 0000000..8309f66
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Inc
+ *
+ * Aneesh V <aneesh@ti.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "bqt.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <math.h>
+
+enum op_type_t {
+       INVALID_OPERATOR = 0,
+       OPERATOR_EXPONENT,
+       OPERATOR_MULT,
+       OPERATOR_DIV,
+       OPERATOR_ADD,
+       OPERATOR_SUBTRACT,
+};
+
+enum op_precedence_t {
+       OP_PREC_EXPONENT = 6,
+       OP_PREC_MULT_DIV = 5,
+       OP_PREC_ADD_SUB = 4,
+
+};
+
+enum op_assoc_t {
+       LEFT_ASSOCIATIVE,
+       RIGHT_ASSOCIATIVE,
+};
+
+/*
+ * Not supporting multi-character operators as of now.
+ * Not used in any bqz files I have seen and it doesn't
+ * look like bqStudio supports any operators other than
+ * the basic ones. So, no need to complicate things by
+ * supporting the full array of C operators.
+ */
+struct operator_t {
+       char op_char;
+       enum op_type_t op_num;
+       enum op_precedence_t prec;
+       enum op_assoc_t assoc;
+       double (*fp)(double op1, double op2);
+};
+
+static double mult(double op1, double op2)
+{
+       return op1 * op2;
+}
+
+static double my_div(double op1, double op2)
+{
+       return op1 / op2;
+}
+
+static double add(double op1, double op2)
+{
+       return op1 + op2;
+}
+
+static double sub(double op1, double op2)
+{
+       return op1 - op2;
+}
+
+static double my_pow(double op1, double op2)
+{
+       return pow(op1, op2);
+}
+
+const struct operator_t operators[] = {
+       {'^',   OPERATOR_EXPONENT,      OP_PREC_EXPONENT, RIGHT_ASSOCIATIVE, my_pow},
+       {'*',   OPERATOR_MULT,          OP_PREC_MULT_DIV, LEFT_ASSOCIATIVE,  mult},
+       {'/',   OPERATOR_DIV,           OP_PREC_MULT_DIV, LEFT_ASSOCIATIVE,  my_div},
+       {'+',   OPERATOR_ADD,           OP_PREC_ADD_SUB,  LEFT_ASSOCIATIVE,  add},
+       {'-',   OPERATOR_SUBTRACT,      OP_PREC_ADD_SUB,  LEFT_ASSOCIATIVE,  sub},
+};
+
+enum tok_type_t {
+       OPERATOR,
+       LEFT_PARENTHESIS,
+       RIGHT_PARENTHESIS,
+       OPERAND_CONST,
+       OPERAND_X,
+};
+
+
+struct token_t {
+       double val;
+       enum tok_type_t type;
+       const struct operator_t *op;
+};
+
+struct queue_t *create_queue()
+{
+       struct queue_t *q = (struct queue_t *) malloc(sizeof(struct queue_t));
+
+       q->head = NULL;
+       q->tail = NULL;
+       q->curr = NULL;
+
+       return q;
+}
+
+void delete_queue(struct queue_t *q)
+{
+       struct node_t *node = q->head;
+       struct node_t *next;
+
+       while (node) {
+               next = node->next;
+               free(node->val_p);
+               free(node);
+               node = next;
+       }
+
+       free(q);
+}
+
+
+static void push_front(struct queue_t *q, void *val_p)
+{
+       struct node_t *node = (struct node_t *) malloc(sizeof(struct node_t));
+
+       node->val_p = val_p;
+       if (q->head)
+               q->head->prev = node;
+       node->next = q->head;
+       node->prev = NULL;
+       q->head = node;
+       if (!q->tail)
+               q->tail = node;
+}
+
+static void push_back(struct queue_t *q, void *val_p)
+{
+       struct node_t *node = (struct node_t *) malloc(sizeof(struct node_t));
+
+       node->val_p = val_p;
+       node->next = NULL;
+       if (q->tail)
+               q->tail->next = node;
+       node->prev = q->tail;
+       q->tail = node;
+       if (!q->head)
+               q->head = node;
+}
+
+static void* pop_front(struct queue_t* q)
+{
+       if (!q->head)
+               return NULL;
+
+       struct node_t *front = q->head;
+       struct node_t *next = q->head->next;
+
+       void *val_p = front->val_p;
+       q->head = next;
+       if (next)
+               next->prev = NULL;
+
+       free(front);
+
+       return val_p;
+}
+
+static void reset_iterator(struct queue_t* q)
+{
+       q->curr = q->head;
+}
+
+static void *get_next(struct queue_t *q)
+{
+       if (!q->curr)
+               return NULL;
+
+       void *val_p = q->curr->val_p;;
+
+       q->curr = q->curr->next;
+
+       return val_p;
+}
+
+static void *front(struct queue_t* q)
+{
+       if (q->head)
+               return q->head->val_p;
+       else
+               return NULL;
+}
+
+#if 0
+static void *back(struct queue_t* q)
+{
+       if (q->tail)
+               return q->tail->val_p;
+       else
+               return NULL;
+}
+#endif
+
+static int get_operator(const char c, struct token_t *tok)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(operators)/sizeof(operators[0]); i++) {
+               if (operators[i].op_char == c) {
+                       tok->op = &operators[i];
+                       return operators[i].op_num;
+               }
+       }
+
+       tok->op = NULL;
+
+       return INVALID_OPERATOR;
+}
+
+struct token_t *get_expr_token(const char *expr, const char **endptr)
+{
+       if (!expr || !(*expr))
+               return NULL;
+
+       struct token_t tok;
+       char c = *expr;
+       char *tmp;
+
+       /* */
+       tmp = (char *) expr + 1;
+
+       if(isdigit(c)) {
+               //d = strtod(expr, &tmp);
+               tok.val = strtod(expr, &tmp);
+               if (expr == tmp) {
+                       pr_err("Not a valid number at :%s\n", expr);
+                       goto error;
+               }
+               tok.type = OPERAND_CONST;
+       } else if (c == 'x' || c == 'X') {
+               tok.type = OPERAND_X;
+       } else if (c == '(') {
+               tok.type = LEFT_PARENTHESIS;
+       } else if (c == ')') {
+               tok.type = RIGHT_PARENTHESIS;
+       } else {
+               if (get_operator(c, &tok) == INVALID_OPERATOR) {
+                       pr_err("Not a valid token at :%s\n", expr);
+                       goto error;
+               }
+               tok.type = OPERATOR;
+       }
+
+       struct token_t *ret_tok = (struct token_t *) malloc(sizeof(struct token_t));
+       if (ret_tok) {
+               *ret_tok = tok;
+               *endptr = tmp;
+       }
+
+       return ret_tok;
+
+error:
+       *endptr = expr;
+       return NULL;
+}
+
+/*
+Logic:
+- Read a token.
+- If the token is a number, then add it to the output queue.
+- If the token is an operator, o1, then:
+       . while there is an operator token, o2, at the top of the operator stack, and either
+               o1 is left-associative and its precedence is less than or equal to that of o2, or
+               o1 is right associative, and has precedence less than that of o2,
+       . then pop o2 off the operator stack, onto the output queue;
+       . push o1 onto the operator stack.
+- If the token is a left parenthesis (i.e. "("), then push it onto the stack.
+- If the token is a right parenthesis (i.e. ")"):
+       . Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue.
+       . Pop the left parenthesis from the stack, but not onto the output queue.
+       . If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.
+- When there are no more tokens to read:
+       . While there are still operator tokens in the stack:
+               If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses.
+               Pop the operator onto the output queue.
+- Exit.
+*/
+struct queue_t *parse_expression(const char *expr)
+{
+       struct queue_t *outq = create_queue();
+       struct queue_t *op_stack = create_queue();
+       struct token_t *tok, *tmp_tok;
+
+       while ((tok = get_expr_token(expr, &expr))) {
+       switch(tok->type) {
+       case OPERAND_CONST:
+       case OPERAND_X:
+               push_back(outq, tok);
+               break;
+       case OPERATOR:
+               while ((tmp_tok = (struct token_t *) front(op_stack))) {
+                       if ((tok->op->assoc == LEFT_ASSOCIATIVE && tok->op->prec <= tmp_tok->op->prec) ||
+                           (tok->op->assoc == RIGHT_ASSOCIATIVE && tok->op->prec < tmp_tok->op->prec)) {
+                               push_back(outq, tmp_tok);
+                               pop_front(op_stack);
+                       } else {
+                               break;
+                       }
+               }
+               push_front(op_stack, tok);
+               //push_front(op_stack, tok);
+               break;
+       case LEFT_PARENTHESIS:
+               push_front(op_stack, tok);
+               break;
+       case RIGHT_PARENTHESIS:
+               while ((tmp_tok = (struct token_t *) pop_front(op_stack)) &&
+                       (tmp_tok->type != LEFT_PARENTHESIS)) {
+                       push_back(outq, tmp_tok);
+               }
+               free(tok);
+               if (tmp_tok) {
+                       /* Left parenthesis, we don't need it anymore either */
+                       free(tmp_tok);
+               } else {
+                       pr_err("Unmatched parenthesis, no left parenthesis matching right parenthesis at:%s\n", expr - 1);
+                       goto error;
+               }
+               break;
+       default:
+               pr_err("Unexpected token just before :%s", expr);
+               goto error;
+               break;
+       }
+       }
+
+       /* End of stream, pop out the remaining opertors from operator stack */
+       while ((tmp_tok = (struct token_t *) pop_front(op_stack)) &&
+               (tmp_tok->type != LEFT_PARENTHESIS)) {
+               push_back(outq, tmp_tok);
+       }
+
+       if (tmp_tok) {
+               pr_err("Unmatched parenthesis, unmatched left parenthesis\n");
+               goto error;
+       }
+
+       delete_queue(op_stack);
+       return outq;
+
+error:
+       delete_queue(op_stack);
+       delete_queue(outq);
+       pr_err("Parsing failed!\n");
+       return NULL;
+}
+
+/*
+ * Evaluate reverse polish notation expression:
+ * rpn_q - Expression in Reverse Polish(postfix) notation
+ * Logic: https://en.wikipedia.org/wiki/Reverse_Polish_notation#Postfix_algorithm
+ * limited to binary operators
+ */
+double evaluate_expr(struct queue_t *rpn_q, double val)
+{
+       double result;
+       struct token_t *tok, *tok_tmp, *opnd1, *opnd2;
+       struct queue_t *opnd_stack = create_queue();
+
+       reset_iterator(rpn_q);
+       while((tok = (struct token_t *) get_next(rpn_q))) {
+               switch(tok->type) {
+               case OPERAND_CONST:
+               case OPERAND_X:
+                       tok_tmp = (struct token_t *) malloc(sizeof(struct token_t));
+                       *tok_tmp = *tok;
+                       if (tok->type == OPERAND_X)
+                               tok_tmp->val = val;
+                       push_front(opnd_stack, tok_tmp);
+                       break;
+               case OPERATOR:
+                       opnd2 = (struct token_t *) pop_front(opnd_stack);
+                       opnd1 = (struct token_t *) front(opnd_stack);
+                       if (!opnd1 || !opnd2) {
+                               pr_err("Not enough operands for operator :%c\n", tok->op->op_char)
+                               return 0;
+                       }
+                       opnd1->val = tok->op->fp(opnd1->val, opnd2->val);
+                       free(opnd2);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       reset_iterator(opnd_stack);
+       opnd1 = (struct token_t *) get_next(opnd_stack);
+       opnd2 = (struct token_t *) get_next(opnd_stack);
+       result = opnd1->val;
+       delete_queue(opnd_stack);
+       if (opnd2) {
+               pr_err("More than one operand remaining in the stack, but no more operators remainig\n");
+               return 0;
+       }
+
+       return result;
+}
diff --git a/gauge-simulator.c b/gauge-simulator.c
new file mode 100644 (file)
index 0000000..2f427d6
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Inc
+ *
+ * Aneesh V <aneesh@ti.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bqt.h"
+
+#ifdef GAUGE_SIMULATOR
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#define GAUGE_SIM_FILE_MAX_LINE_LEN ((32 + 4) * 3)
+const char *gauge_sim_file = "gauge_sim_dm.txt";
+int read_params_block(struct csv_info_t *csv, struct gauge_info_t *gauge);
+int write_params_block(struct csv_info_t *csv, struct gauge_info_t *gauge);
+
+static uint16_t control_read(struct gauge_info_t *gauge, uint16_t ctrl_cmd)
+{
+       return 0;
+}
+
+
+static int open_dm_virtual(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       return 1;
+}
+
+static void close_dm_virtual(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       return;
+}
+
+static void reset_virtual(struct gauge_info_t *gauge)
+{
+       return;
+}
+
+long int get_block_line(struct file_info_t *file, char **line,
+       uint8_t subclass, uint8_t blk_ind)
+{
+       long int pos = ftell(file->fd);
+
+       while ((*line = read_line(file))) {
+               char *str = *line;
+               if (strtoul(str, &str, 16) == subclass &&
+                       strtoul(str, &str, 16) == blk_ind)
+                       return pos;
+               pos = ftell(file->fd);
+       }
+
+       return -1;
+}
+
+static int read_dm_block(struct gauge_info_t *gauge, uint8_t subclass,
+       uint8_t blk_ind, uint8_t *buf)
+{
+       char *line, *tmp;
+       long int pos;
+       int i;
+
+       struct file_info_t *file = create_file(gauge_sim_file, "r",
+               GAUGE_SIM_FILE_MAX_LINE_LEN);
+
+       pos = get_block_line(file, &line, subclass, blk_ind);
+
+       if (pos == -1) {
+               for (i = 1; i < 33; i++)
+                       buf[i] = 0;
+       } else {
+               for (i = 1; i < 33; i++)
+                       buf[i] = (uint8_t) strtoul(&line[(i - 1) * 3 + 5], &tmp, 16);
+       }
+
+       //fclose(file->fd);
+
+       free_file(file);
+
+       return 1;
+}
+
+static int write_dm_block(struct gauge_info_t *gauge, uint8_t subclass,
+       uint8_t blk_ind, uint8_t *buf)
+{
+       char *line;
+       long int pos, i;
+
+       struct file_info_t *file = create_file(gauge_sim_file, "r+",
+               GAUGE_SIM_FILE_MAX_LINE_LEN);
+
+       pos = get_block_line(file, &line, subclass, blk_ind);
+
+       if (pos == -1)
+               fseek(file->fd, 0, SEEK_END);
+       else
+               fseek(file->fd, pos, SEEK_SET);
+
+       fprintf(file->fd, "%02x %02x", subclass, blk_ind);
+       for (i = 1; i < 33; i++)
+               fprintf(file->fd, " %02x", buf[i]);
+
+       fprintf(file->fd, "\n");
+       fflush(file->fd);
+
+       free_file(file);
+
+       return 1;
+}
+
+static void sleep_ms_simulator(uint16_t ms)
+{
+       usleep(ms * 1000);
+}
+
+int init_gauge_interface(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       //struct gauge_info_t *gauge = &csv->gauge;
+
+       gauge->device_num = 0x0545;
+       gauge->fw_version = 0x0502;
+       gauge->endianness = BYTEORDER_BE;
+       gauge->ic = GAUGE_IC_X;
+       gauge->write_params = write_params_block;
+       gauge->read_params = read_params_block;
+       gauge->open_dm = open_dm_virtual;
+       gauge->close_dm = close_dm_virtual;
+       gauge->reset = reset_virtual;
+       gauge->sleep_ms = sleep_ms_simulator;
+
+       return 1;
+}
+#endif
diff --git a/gauge.c b/gauge.c
new file mode 100644 (file)
index 0000000..d4e954e
--- /dev/null
+++ b/gauge.c
@@ -0,0 +1,1021 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Inc
+ *
+ * Aneesh V <aneesh@ti.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "bqt.h"
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define SEAL_UNSEAL_POLLING_LIMIT_MS   1000
+
+int do_read(struct gauge_info_t *gauge, uint8_t *buf, uint8_t len)
+{
+       int i = 0;
+       uint8_t addr = gauge->slave_addr;
+
+       while (i++ < 4) {
+               if (gauge->read(gauge, addr, buf, len))
+                       return 1;
+               gauge->sleep_ms(10);
+               pr_err("Retrying read..\n")
+       }
+
+
+       return 0;
+}
+
+int do_write(struct gauge_info_t *gauge, uint8_t *buf, uint8_t len)
+{
+       int i = 0;
+       uint8_t addr = gauge->slave_addr;
+
+       while (i++ < 4) {
+               if (gauge->write(gauge, addr, buf, len))
+                       return 1;
+               gauge->sleep_ms(10);
+               pr_err("Retrying write..\n")
+       }
+
+       return 0;
+}
+
+static uint16_t read_word(struct gauge_info_t *gauge, uint8_t reg)
+{
+       /* Let's not assume Endianness */
+       uint8_t buf[3];
+       uint16_t reg_val = 0;
+
+       buf[0] = reg;
+
+       if (do_read(gauge, buf, 2)) {
+               reg_val = buf[2] << 8;
+               reg_val |= buf[1];
+       }
+
+       return reg_val;
+}
+
+static int write_word(struct gauge_info_t *gauge, uint8_t reg, uint16_t reg_val)
+{
+       /* Let's not assume Endianness */
+       uint8_t buf[3];
+
+       buf[0] = reg;
+       buf[1] = (uint8_t) (reg_val & 0xFF);
+       buf[2] = (uint8_t) ((reg_val & 0xFF00) >> 8);
+
+
+       return do_write(gauge, buf, 2);
+}
+
+static uint8_t read_byte(struct gauge_info_t *gauge, uint8_t reg)
+{
+       /* Let's not assume Endianness */
+       uint8_t buf[2];
+
+       buf[0] = reg;
+
+       if (do_read(gauge, buf, 1))
+               return buf[1];
+
+       return 0;
+}
+
+static int write_byte(struct gauge_info_t *gauge, uint8_t reg, uint8_t val)
+{
+       /* Let's not assume Endianness */
+       uint8_t buf[2];
+
+       buf[0] = reg;
+       buf[1] = val;
+
+       return do_write(gauge, buf, 1);
+}
+
+static void control_write(struct gauge_info_t *gauge, uint16_t val)
+{
+       write_word(gauge, REG_CONTROL, val);
+}
+
+static uint16_t control_read(struct gauge_info_t *gauge, uint16_t ctrl_cmd)
+{
+       control_write(gauge, ctrl_cmd);
+
+       gauge->sleep_ms(5);
+
+       return read_word(gauge, REG_CONTROL);
+}
+
+static uint16_t control_status(struct gauge_info_t *gauge)
+{
+       return control_read(gauge, CTRL_CMD_STATUS);
+}
+
+static uint16_t device_type(struct gauge_info_t *gauge)
+{
+       return control_read(gauge, CTRL_CMD_DEVICE_TYPE);
+}
+
+static uint16_t fw_version(struct gauge_info_t *gauge)
+{
+       return control_read(gauge, CTRL_CMD_FW_VERSION);
+}
+
+static int sealed(struct gauge_info_t *gauge)
+{
+       return control_status(gauge) & CTRL_STATUS_SS;
+}
+
+static int fullaccess_sealed(struct gauge_info_t *gauge)
+{
+       if (gauge->family == 0x8101)
+               return 0;
+
+       return control_status(gauge) & CTRL_STATUS_FAS;
+}
+
+static int seal(struct gauge_info_t *gauge)
+{
+       int i = 0;
+       if (sealed(gauge))
+               return 1;
+
+       control_write(gauge, CTRL_CMD_SEAL);
+
+       while (i < SEAL_UNSEAL_POLLING_LIMIT_MS/10) {
+               i++;
+               if (sealed(gauge)) {
+                       pr_info("sealed the device\n");
+                       break;
+               }
+               gauge->sleep_ms(10);
+       }
+
+       if (i == SEAL_UNSEAL_POLLING_LIMIT_MS / 10) {
+               pr_err("sealing failed\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static int do_unseal(struct gauge_info_t *gauge, uint32_t key, int fullaccess)
+{
+       int i = 0;
+
+       if ((fullaccess && !fullaccess_sealed(gauge)) ||
+               (!fullaccess && !(sealed(gauge))))
+               return 1;
+
+
+       while (i < SEAL_UNSEAL_POLLING_LIMIT_MS/20) {
+               control_write(gauge, (uint16_t) (key & 0xFFFF));
+               gauge->sleep_ms(5);
+               control_write(gauge, (uint16_t) ((key & 0xFFFF0000) >> 16));
+               gauge->sleep_ms(5);
+               if ((fullaccess && !fullaccess_sealed(gauge)) ||
+                       (!fullaccess && !(sealed(gauge)))) {
+                       pr_info("unsealed %s the device..\n",
+                               fullaccess ? "fullaccess" : "");
+                       break;
+               }
+               gauge->sleep_ms(10);
+               i++;
+       }
+
+       if (i == SEAL_UNSEAL_POLLING_LIMIT_MS / 20) {
+               pr_err("unsealing %s failed!\n",
+                       fullaccess ? "fullaccess" : "");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int unseal(struct gauge_info_t *gauge)
+{
+       return do_unseal(gauge, gauge->unseal_key, 0);
+}
+
+static int unseal_fullaccess(struct gauge_info_t *gauge)
+{
+       return do_unseal(gauge, gauge->fullacccess_key, 1);
+}
+
+static uint8_t native_endianness() {
+    int i = 1;
+    char *p = (char *)&i;
+
+    if (p[0] == 1)
+        return BYTEORDER_LE;
+    else
+        return BYTEORDER_BE;
+}
+
+static void reverse_byteorder(uint8_t *val, int len)
+{
+       int i;
+       uint8_t tmp;
+
+       for (i = 0; i < len / 2; i++) {
+               tmp = val[i];
+               val[i] = val[len - i - 1];
+               val[len - i - 1] = tmp;
+       }
+}
+
+static void copy_reverse_byteorder(uint8_t *dest, uint8_t *src, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++)
+               dest[i] = src[len - i - 1];
+}
+
+/*
+ * Convert from a X single to an IEEE754 floating point
+ * number in Little Endian format
+ */
+float x_float_to_ieee(uint8_t *data)
+{
+       float res;
+       uint8_t *pK;
+
+       pK=(unsigned char*)&res;
+
+       *pK=*(data+3);
+       *(pK+1)=*(data+2);
+       *(pK+2)=*(data+1) & 0x7f;
+       *(pK+3)=((*data) >> 1 )- 1;
+
+       if(*data & 0x01) *(pK+2) |= 0x80;
+
+       if(*(data+1) & 0x80) *(pK+3) |= 0x80;
+
+       return res;
+}
+
+
+/* Convert from IEEE754 single to a x float */
+static void ieee_float_to_x(uint8_t *dest, float ff)
+{
+       unsigned char *f;
+       f=(unsigned char*)&ff;
+
+       *dest=((*(f+3))<<1)+2;
+       *(dest+1)=(*(f+2)) & 0x7f;
+       *(dest+2)=*(f+1);
+       *(dest+3)=*f;
+
+       if(*(f+3) & 0x80)
+       *(dest+1) |= 0x80;
+
+       if(*(f+2) & 0x80)
+       *dest |=1;
+}
+
+static uint8_t checksum(uint8_t *data)
+{
+       uint16_t sum = 0;
+       int i;
+
+       for (i = 0; i < 32; i++)
+               sum += data[i];
+
+       sum &= 0xFF;
+
+       return 0xFF - sum;
+}
+
+/*
+ * !!!!! buf should be 33 bytes long !!!!!
+ * 1 byte for the register address and 32 bytes for data,
+ * write made zero-copy this way
+ */
+static int do_rw_dm_block(struct gauge_info_t *gauge, uint8_t subclass,
+       uint8_t blk_ind, uint8_t *buf, int write, uint32_t delay)
+{
+       int err = 0;
+       uint16_t cksum, cksum_calc;
+
+       err |= !write_byte(gauge, REG_BLOCK_DATA_CONTROL, 0);
+       err |= !write_byte(gauge, REG_BLOCK_DATA_CLASS, subclass);
+       err |= !write_byte(gauge, REG_DATA_BLOCK, blk_ind);
+       gauge->sleep_ms(5);
+       if (write) {
+               buf[0] = REG_BLOCK_DATA;
+               err |= !do_write(gauge, buf, 32);
+
+               /* Write checksum - this is where we need a big delay */
+               cksum_calc = checksum(&buf[1]);
+               err |= !write_byte(gauge, REG_BLOCK_DATA_CHECKSUM, cksum_calc);
+               gauge->sleep_ms(delay);
+
+               /* Readback checksum and compare */
+               err |= !write_byte(gauge, REG_DATA_BLOCK, blk_ind);
+               gauge->sleep_ms(5);
+               cksum = read_byte(gauge, REG_BLOCK_DATA_CHECKSUM);
+               if (cksum != cksum_calc) {
+                       pr_err("checksum failure on write cksum 0x%02x cksum_calc 0x%02x\n",
+                               cksum, cksum_calc);
+                       err = 1;
+               }
+       } else {
+               buf[0] = REG_BLOCK_DATA;
+               err |= !do_read(gauge, buf, 32);
+
+               /* Read checksum and compare */
+               cksum = read_byte(gauge, REG_BLOCK_DATA_CHECKSUM);
+               cksum_calc = checksum(&buf[1]);
+               if (cksum != cksum_calc) {
+                       pr_err("checksum failure on read cksum 0x%02x cksum_calc 0x%02x\n",
+                               cksum, cksum_calc);
+                       err = 1;
+               }
+       }
+
+       if (err) {
+               pr_err("error accessing subclass 0x%02x blk_ind 0x%02x write %d\n",
+                       subclass, blk_ind, write);
+       }
+
+       return !err;
+}
+
+static int rw_dm_block(struct gauge_info_t *gauge, uint8_t subclass,
+       uint8_t blk_ind, uint8_t *buf, int write)
+{
+       int i = 0;
+       uint32_t delay = 300;
+
+       while ((i++ < 5)) {
+               if (do_rw_dm_block(gauge, subclass, blk_ind, buf, write, delay))
+                       return 1;
+
+               /* increase the delay by 100ms at every step */
+               delay += 100;
+               pr_err("retrying %s block..\n", write == 1 ? "write" : "read");
+       }
+
+       return 0;
+}
+
+static int read_dm_block(struct gauge_info_t *gauge, uint8_t subclass,
+       uint8_t blk_ind, uint8_t *buf)
+{
+       int ret = rw_dm_block(gauge, subclass, blk_ind, buf, 0);
+
+       return ret;
+}
+
+static int write_dm_block(struct gauge_info_t *gauge, uint8_t subclass,
+       uint8_t blk_ind, uint8_t *buf)
+{
+       return rw_dm_block(gauge, subclass, blk_ind, buf, 1);
+}
+
+int read_params_block(struct csv_info_t *csv, struct gauge_info_t *gauge)
+{
+       unsigned int i;
+       int err = 0;
+       struct param_t *params = csv->params;
+       uint8_t subclass, blk_ind, offset;
+       uint16_t blk_id, blk_id_prev = 0;
+       uint8_t buf[65], tmp;
+       union val_t tmp_val;
+       uint8_t host_endianness = native_endianness();
+       int read_twoblocks = 0;
+
+       pr_info("reading params..\n");
+
+       for (i = 0; !err && i < csv->num_params; i++) {
+               subclass = (uint8_t) ((params[i].offset & 0xFF000000) >> 24);
+               offset = (uint8_t) (params[i].offset & 0xFF);
+
+               /* block number within subclass */
+               blk_ind = offset >> 5;
+
+               /* offset within the block */
+               offset = offset & 0x1F;
+               if (offset + params[i].data_len > 32) {
+                       /*
+                        * Parameter overflowing to the next block.
+                        * Read that block too
+                        */
+                       read_twoblocks = 1;
+                       tmp = buf[32];
+                       err |= !read_dm_block(gauge, subclass, blk_ind + 1, &buf[32]);
+                       buf[32] = tmp;
+               }
+
+               /*
+                * unique id for the block:
+                * combination of subclass and blk_ind. Useful to uniquely
+                * identify the blocks and figure out when it's time to read
+                * the next block
+                */
+               blk_id = (subclass << 8) | blk_ind;
+
+               if (i == 0 || blk_id != blk_id_prev) {
+                       err |= !read_dm_block(gauge, subclass, blk_ind, buf);
+                       if (err)
+                               goto end;
+               }
+
+               /*
+                * note "offset + 1" below. buf[0] contains the
+                * reg address: BLOCK_DATA
+                * x_float_to_ieee returns the output in IEEE little Endian,
+                * so convert to Big Endian if the host is Big Endian.
+                */
+               tmp_val = params[i].val;
+
+               /*
+                * zero it out before reading new value
+                * Due to the way we are printing them it's important
+                * not to have any stale bits at the higher nibbles for
+                * 16-bit and 8-bit values. Stale bits are possible
+                * because we do not limit check in the input file
+                * in case of export
+                */
+               params[i].val.u = 0;
+               if (params[i].raw_type == DATATYPE_F) {
+                       params[i].val.f = x_float_to_ieee(&buf[offset + 1]);
+                       if (host_endianness == BYTEORDER_BE)
+                               reverse_byteorder((uint8_t*) &params[i].val, params[i].data_len);
+               } else if (params[i].raw_type == DATATYPE_S) {
+                       memcpy(params[i].val_s, &buf[offset + 1], params[i].data_len);
+               } else if (gauge->endianness != host_endianness) {
+                       /* Fix byte ordering (Endianness) for integral numerical types */
+                       copy_reverse_byteorder((uint8_t*) &params[i].val, &buf[offset + 1], params[i].data_len);
+               } else {
+                       memcpy(&params[i].val, &buf[offset + 1], params[i].data_len);
+               }
+
+               if (params[i].raw_type == DATATYPE_S) {
+                       /* Null terminate at the end of buffer for safety */
+                       params[i].val_s[params[i].data_len] = '\0';
+                       /* Put length at first byte of the buffer */
+                       params[i].val_s[0] = (uint8_t) strlen(&params[i].val_s[1]);
+               }
+
+               /*
+                * When we are exporting, we check the limits,
+                * but do not abort the operation if there is
+                * an error
+                */
+               check_limits(&params[i]);
+
+               /* Update the value string if a binary comparison with old value fails */
+               if (tmp_val.u != params[i].val.u)
+                       err |= !update_value_string(&params[i], csv->value_fld_ind);
+
+               blk_id_prev = blk_id;
+               if (read_twoblocks) {
+                       /* now use the overflow buffer as the regular buffer */
+                       memcpy(&buf[1], &buf[33], 32);
+                       blk_id_prev++;
+                       read_twoblocks = 0;
+               }
+       }
+
+end:
+       if (err)
+               pr_err("FAILED!!\n");
+
+       pr_info("reading params successful!\n");
+
+       return !err;
+}
+
+
+
+int write_params_block(struct csv_info_t *csv, struct gauge_info_t *gauge)
+{
+       unsigned int i;
+       struct param_t *params = csv->params;
+       uint8_t subclass = 0, blk_ind = 0, offset;
+       uint16_t blk_id, blk_id_prev = 0;
+       uint8_t buf[65], tmp;
+       union val_t tmp_val;
+       uint8_t host_endianness = native_endianness();
+       int read_twoblocks = 0, err = 0;
+
+       pr_info("writing params..\n");
+
+       for (i = 0; !err && i < csv->num_params; i++) {
+               if (!check_limits(&params[i]))
+                       goto end;
+
+               subclass = (uint8_t) ((params[i].offset & 0xFF000000) >> 24);
+               offset = (uint8_t) (params[i].offset & 0xFF);
+
+               /* block number within subclass */
+               blk_ind = offset >> 5;
+
+               /* offset within the block */
+               offset = offset & 0x1F;
+               if (offset + params[i].data_len > 32) {
+                       /*
+                        * Parameter overflowing to the next block.
+                        * Read that block too
+                        */
+                       read_twoblocks = 1;
+                       tmp = buf[32];
+                       err |= !read_dm_block(gauge, subclass, blk_ind + 1, &buf[32]);
+                       buf[32] = tmp;
+               }
+
+               /*
+                * unique id for the block:
+                * combination of subclass and blk_ind. Useful to uniquely
+                * identify the blocks and figure out when it's time to read
+                * the next block
+                */
+               blk_id = (subclass << 8) | blk_ind;
+
+               if (i == 0 || blk_id != blk_id_prev) {
+
+                       /*
+                        * if this is the first parameter the buf is not valid yet
+                        * in all other cases it's valid
+                        */
+                       if (i != 0) {
+                               err |= !write_dm_block(gauge, (uint8_t) ((blk_id_prev & 0xFF00) >> 8),
+                                       (uint8_t) (blk_id_prev & 0xFF), buf);
+                       }
+
+                       if (!err)
+                               err |= !read_dm_block(gauge, subclass, blk_ind, buf);
+                       if (err)
+                               goto end;
+                }
+
+               /*
+                * note "offset + 1" below. buf[0] contains the
+                * reg address: BLOCK_DATA
+                * ieee_float_to_x returns the output in x format,
+                * so no Endian conversion required after that.
+                */
+               tmp_val = params[i].val;
+
+               if (gauge->ic == GAUGE_IC_X && params[i].raw_type == DATATYPE_F) {
+                       /* ieee_float_to_x assumes the input float to be in Little Endian */
+                       if (host_endianness == BYTEORDER_BE)
+                               reverse_byteorder((uint8_t*) &tmp_val, params[i].data_len);
+                       ieee_float_to_x(&buf[offset + 1], tmp_val.f);
+
+               } else if (params[i].raw_type == DATATYPE_S) {
+                       memcpy(&buf[offset + 1], params[i].val_s, params[i].data_len);
+               } else if (gauge->endianness != host_endianness) {
+                       copy_reverse_byteorder(&buf[offset + 1], (uint8_t*) &params[i].val, params[i].data_len);
+               } else {
+                       memcpy(&buf[offset + 1], &params[i].val, params[i].data_len);
+               }
+
+               blk_id_prev = blk_id;
+               if (read_twoblocks) {
+                       /*
+                        * now write the first block and use the overflow buffer
+                        * as the regular buffer
+                        */
+                       err |= !write_dm_block(gauge, (uint8_t) ((blk_id_prev & 0xFF00) >> 8),
+                               (uint8_t) (blk_id_prev & 0xFF), buf);
+                       memcpy(&buf[1], &buf[33], 32);
+                       blk_id_prev++;
+                       read_twoblocks = 0;
+               }
+       }
+
+       err |= !write_dm_block(gauge, subclass, blk_ind, buf);
+
+end:
+       if (err)
+               pr_err("FAILED!!\n");
+
+       pr_info("writing params successful!\n");
+
+       return !err;
+}
+
+int read_regs(struct csv_info_t *csv, struct gauge_info_t *gauge)
+{
+       unsigned int i;
+       int err = 0;
+       struct param_t *params = csv->params;
+       uint8_t buf[5];
+       uint8_t host_endianness = native_endianness();
+       union val_t tmp_val;
+       uint8_t ctrl, reg;
+       uint16_t ctrl_reg;
+
+       pr_info("reading regs..\n");
+
+       memset(buf, 0, 5);
+
+       for (i = 0; !err && i < csv->num_params; i++) {
+               if (params[i].data_len > 4) {
+                       pr_err(" %s: data length %d greater than max allowed 4\n")
+                       return 0;
+               }
+
+               tmp_val = params[i].val;
+
+               ctrl = (uint8_t) ((params[i].offset & 0x01000000) >> 24);
+               if (ctrl) {
+                       if (params->data_len > 2) {
+                               pr_err("control command read can not be more"
+                               " than 2 bytes long. Truncating to 2 bytes..\n");
+                               params->data_len = 2;
+                       }
+                       ctrl_reg = (uint16_t) (params[i].offset & 0xFFFF);
+                       params[i].val.u = control_read(gauge, ctrl_reg);
+               } else {
+                       reg = (uint8_t) (params[i].offset & 0xFF);
+                       buf[0] = reg;
+                       err |= !do_read(gauge, buf, params[i].data_len);
+
+                       /* zero it out before reading new value */
+                       params[i].val.u = 0;
+                       if (params[i].raw_type == DATATYPE_F) {
+                               reverse_byteorder((uint8_t*) &buf[1], params[i].data_len);
+                               params[i].val.f = x_float_to_ieee(&buf[1]);
+                               if (host_endianness == BYTEORDER_BE)
+                                       reverse_byteorder((uint8_t*) &params[i].val, params[i].data_len);
+                       } else if (params[i].raw_type == DATATYPE_S) {
+                               memcpy(params[i].val_s, &buf[1], params[i].data_len);
+                       } else if (host_endianness == BYTEORDER_BE) {
+                               /* Fix byte ordering (Endianness) for integral numerical types */
+                               copy_reverse_byteorder((uint8_t*) &params[i].val, &buf[1], params[i].data_len);
+                       } else {
+                               memcpy(&params[i].val, &buf[1], params[i].data_len);
+                       }
+               }
+
+               /* Update the value string if a binary comparison with old value fails */
+               if (tmp_val.u != params[i].val.u)
+                       err |= !update_value_string(&params[i], csv->value_fld_ind);
+       }
+
+
+       if (err)
+               pr_err("FAILED!!\n");
+
+       pr_info("reading regs successful!\n");
+
+       return !err;
+}
+
+static int seal_after_dm_access(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       /* By default return to the previous seal state */
+       int seal_req = gauge->orig_seal_status;
+       int err = 0;
+       const char *exit_seal = get_cmdline_argument(argc, argv, "--exit-seal=", 1);
+
+       if (exit_seal) {
+               if (strcmp(exit_seal, "seal") == 0)
+                       seal_req = 1;
+               else if (strcmp(exit_seal, "unseal") == 0)
+                       seal_req = 0;
+               else if (strcmp(exit_seal, "original") == 0)
+                       seal_req = gauge->orig_seal_status;
+               else
+                       pr_err("undetected --exit-seal= request\n");
+       }
+
+       if (seal_req) {
+               err |= seal(gauge);
+       } else {
+               err |= unseal(gauge);
+               err |= unseal_fullaccess(gauge);
+       }
+
+       return err;
+}
+
+static uint32_t gauge_fmly_from_user(int argc, char **argv)
+{
+       uint32_t ret = 0;
+       char *tmp;
+       const char *family = get_cmdline_argument(argc, argv, "--gauge-family=", 1);
+
+       if (!family)
+               return 0;
+
+       if (strstr(family, "bq") == family )
+               ret = strtoul(&family[2], &tmp, 16);
+
+       pr_dbg("family from user %s\n", family);
+
+       if (!ret)
+               pr_err("bad input for --gauge-family\n");
+
+       return ret;
+}
+
+static uint32_t autodetect_gauge_family(uint16_t dev)
+{
+       uint32_t family = 0;
+
+       switch(dev) {
+       case 0x0500:
+       case 0x0501:
+       case 0x0505:
+                family = 0x8032;
+                break;
+       case 0x0510:
+       case 0x0541:
+       case 0x0410:
+                family = 0x8034;
+                break;
+       case 0x0520:
+       case 0x0545:
+       case 0x0530:
+       case 0x0531:
+       case 0x0532:
+       case 0x0620:
+                family = 0x8035;
+                break;
+       case 0x0425:
+                family = 0x8036;
+                break;
+       case 0x0546:
+       case 0x0741:
+       case 0x0742:
+                family = 0x8037;
+                break;
+       case 0x0421:
+       /*case 0x0411:
+       case 0x0441:*/
+       case 0x0621:
+                family = 0x8101;
+                break;
+       default:
+               pr_err("uable to auto-detect gauge family\n");
+               break;
+       }
+
+       pr_dbg("autodetected family bq%04x\n", family);
+
+       return family;
+}
+
+static int get_gauge_family(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       uint32_t usr_fmly, det_fmly;
+
+       usr_fmly = gauge_fmly_from_user(argc, argv);
+       det_fmly = autodetect_gauge_family(gauge->device_num);
+       if (det_fmly) {
+               gauge->family = det_fmly;
+               if (usr_fmly && (usr_fmly != det_fmly)) {
+                       pr_err("User provided gauge-family does not"
+                       "match with autodetected family. Using autodetected"
+                       "family usr 0x%0x auto 0x%0x\n",
+                       usr_fmly, det_fmly);
+               }
+       } else if (usr_fmly) {
+               gauge->family = usr_fmly;
+       } else if (gauge->op == OP_BQFS_FLASH){
+               gauge->family = 0x8035;
+               pr_err("Unable to get gauge family for bqfs flashing. Assuming 0x8035..\n");
+       } else {
+               pr_err("unable to detect gauge family. Aborting..\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+static void reset_gauge(struct gauge_info_t *gauge)
+{
+       pr_info("resetting gauge..\n");
+       control_write(gauge, CTRL_CMD_RESET);
+}
+
+
+static int open_dm_flash(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       long long int res;
+       int err_ss = 0, err_fas = 0, keys_in_gg = 0;
+       int ss, fas;
+
+       pr_dbg(">>\n");
+
+       ss = sealed(gauge);
+       fas = fullaccess_sealed(gauge);
+       gauge->orig_seal_status = (uint8_t) (ss || fas);
+       if (!ss && !fas)
+               return 1;
+
+       const char *unseal_key_cmdline =
+               get_cmdline_argument(argc, argv, "--unseal-key=", 1);
+       const char *fullaccess_key_cmdline =
+               get_cmdline_argument(argc, argv, "--fullaccess-key=", 1);
+
+       keys_in_gg = gauge->unseal_key_found || gauge->fullaccess_key_found;
+
+       if (unseal_key_cmdline && extract_int(unseal_key_cmdline, 16, &res)) {
+               gauge->unseal_key = (uint32_t) res;
+               gauge->unseal_key_found = 1;
+               pr_dbg("Unseal key from command line 0x%08x\n",
+                       gauge->unseal_key);
+       }
+
+       if (fullaccess_key_cmdline &&  extract_int(fullaccess_key_cmdline, 16, &res)) {
+               gauge->fullacccess_key = (uint32_t) res;
+               gauge->fullaccess_key_found = 1;
+               pr_dbg("Full access key from command line 0x%08x\n",
+                       gauge->fullacccess_key);
+       }
+
+       if (ss && gauge->unseal_key_found) {
+               err_ss = !unseal(gauge);
+       } else if (ss && !gauge->unseal_key_found && gauge->op != OP_BQFS_FLASH) {
+               pr_err("unseal key not provided\n");
+               err_ss = 1;
+       }
+
+       if (fas && gauge->fullaccess_key_found) {
+               /* Ignore the error if the keys are not in gg */
+               err_fas = keys_in_gg && !unseal_fullaccess(gauge);
+       } else if (fas && keys_in_gg && !gauge->fullaccess_key_found) {
+               pr_err("Full access key not provided even though read/write of keys is requested\n");
+               err_fas = 1;
+       }
+
+       pr_dbg("<<\n");
+
+       return !(err_ss || err_fas);
+}
+
+static void close_dm_flash(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       seal_after_dm_access(gauge, argc, argv);
+}
+
+#define CFG_UPDATE_POLLING_RETRY_LIMIT_MS      5000
+
+static void reset_gauge_rom(struct gauge_info_t *gauge)
+{
+       pr_err("Doing reset of ROM gauge is not a good idea!! Ignoring..\n");
+}
+
+static int enter_cfgupdate_mode(struct gauge_info_t *gauge)
+{
+       int i = 0;
+       uint16_t flags;
+
+       control_write(gauge, CTRL_CMD_ROM_GAUGE_SET_CFGUPDATE);
+
+       while (i < CFG_UPDATE_POLLING_RETRY_LIMIT_MS / 100) {
+               i++;
+               flags = read_word(gauge, ROM_GAUGE_FLAGS);
+               if (flags & ROM_GAUGE_FLAG_CFGUPDATE_MODE)
+                       break;
+               gauge->sleep_ms(100);
+       }
+
+       if (i == CFG_UPDATE_POLLING_RETRY_LIMIT_MS / 100) {
+               pr_err("failed flags %04x\n", flags);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int exit_cfgupdate_mode(struct gauge_info_t *gauge)
+{
+       int i = 0;
+       uint16_t flags;
+
+       control_write(gauge, CTRL_CMD_ROM_GAUGE_EXIT_CFGUPDATE);
+
+       while (i < CFG_UPDATE_POLLING_RETRY_LIMIT_MS / 100) {
+               i++;
+               flags = read_word(gauge, ROM_GAUGE_FLAGS);
+               if (!(flags & ROM_GAUGE_FLAG_CFGUPDATE_MODE))
+                       break;
+               gauge->sleep_ms(100);
+       }
+
+       if (i == CFG_UPDATE_POLLING_RETRY_LIMIT_MS / 100) {
+               pr_err("failed %04x\n", flags);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int rom_itpor(struct gauge_info_t *gauge)
+{
+       return read_word(gauge, ROM_GAUGE_FLAGS) & ROM_GAUGE_FLAG_ITPOR;
+}
+
+static int open_dm_rom(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       int ret = 1;
+
+       gauge->unseal_key = ROM_GAUGE_UNSEAL_KEY;
+       gauge->unseal_key_found = 1;
+       gauge->fullaccess_key_found = 1;
+       gauge->orig_seal_status = (uint8_t) sealed(gauge);
+
+       if (gauge->orig_seal_status)
+               ret = unseal(gauge);
+
+       if (ret && gauge->op == OP_DM_WRITE) {
+               return enter_cfgupdate_mode(gauge);
+       }
+
+       return ret;
+}
+
+static void close_dm_rom(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       if (gauge->op == OP_DM_WRITE)
+               exit_cfgupdate_mode(gauge);
+
+       seal_after_dm_access(gauge, argc, argv);
+}
+
+static int gauge_family_specific_init(struct gauge_info_t *gauge)
+{
+       switch(gauge->family) {
+#if (defined(BQ8032) || defined(BQ8034) || defined(BQ8035) || defined(BQ8036) || defined(BQ8037))
+       case 0x8032:
+       case 0x8034:
+       case 0x8035:
+       case 0x8036:
+       case 0x8037:
+               gauge->ic = GAUGE_IC_X;
+               gauge->endianness = BYTEORDER_BE;
+               gauge->open_dm = open_dm_flash;
+               gauge->close_dm = close_dm_flash;
+               gauge->write_params = write_params_block;
+               gauge->read_params = read_params_block;
+               gauge->reset = reset_gauge;
+               break;
+#endif
+#if defined(BQ8101)
+       case 0x8101:
+               gauge->ic = GAUGE_IC_X;
+               gauge->endianness = BYTEORDER_BE;
+               gauge->open_dm = open_dm_rom;
+               gauge->close_dm = close_dm_rom;
+               gauge->write_params = write_params_block;
+               gauge->read_params = read_params_block;
+               gauge->reset = reset_gauge_rom;
+               gauge->itpor = rom_itpor;
+               gauge->fw_ver_bld = (uint8_t)
+                       control_read(gauge, CTRL_CMD_ROM_GAUGE_DM_CODE);
+               break;
+#endif
+       default:
+               pr_err("unsupported gauge family 0x%0x\n", gauge->family);
+               return 0;
+       }
+
+       return 1;
+}
+
+#ifndef GAUGE_SIMULATOR
+int init_gauge_interface(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       if (!setup_comm_callbacks(gauge))
+               return 0;
+
+       if (!gauge->init_comm_interface(gauge, argc, argv))
+               return 0;
+
+       /* Get device type and FW version */
+       gauge->device_num = device_type(gauge);
+       gauge->fw_version = fw_version(gauge);
+
+       if (gauge->op == OP_REG_DUMP)
+               return 1;
+
+       if (!get_gauge_family(gauge, argc, argv))
+               return 0;
+
+       return gauge_family_specific_init(gauge);
+}
+#endif
+
diff --git a/interface-linux-i2c.c b/interface-linux-i2c.c
new file mode 100644 (file)
index 0000000..ad17f67
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Inc
+ *
+ * Aneesh V <aneesh@ti.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "bqt.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#define I2C_BUS                        2
+#define I2C_DEV_FILE_BASE      "/dev/i2c-"
+#define I2C_SLAVE_ADDR_DEFUALT 0xAA
+#define SYSFS_POLL_INTVL "/sys/module/bq27x00_battery/parameters/poll_interval"
+
+
+struct i2c_info_t {
+       int     i2c_file;
+       int     drvr_poll_intvl;
+};
+
+static int linux_i2c_read(struct gauge_info_t *gauge, uint8_t slave_addr, uint8_t *buf, uint8_t len)
+{
+       int ret;
+       struct i2c_rdwr_ioctl_data i2c_data;
+       /* msg[0] for write command and msg[1] for read command */
+       struct i2c_msg msgs[2];
+       uint8_t reg = buf[0];
+       int i2c_file = ((struct i2c_info_t *)(gauge->interface))->i2c_file;
+
+       /*
+        * Write part
+        */
+       /* Linux expects 7 bit slave address */
+       msgs[0].addr = slave_addr >> 1;
+       /* buf[0] contains reg address */
+       msgs[0].buf = buf;
+       msgs[0].flags = 0;
+       msgs[0].len = 1;
+
+       /*
+        * Read part
+        */
+       msgs[1].addr = msgs[0].addr;
+       /* data starts from buf[1] */
+       msgs[1].buf = &buf[1];
+       msgs[1].flags = I2C_M_RD;
+       msgs[1].len = len;
+
+       i2c_data.nmsgs = 2;
+       i2c_data.msgs = msgs;
+       ret = ioctl(i2c_file, I2C_RDWR, &i2c_data);
+       if (ret < 0) {
+               fprintf(stderr, "I2C read failed reg %02x len %d err %d\n",
+                       reg, len, ret);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int linux_i2c_write(struct gauge_info_t *gauge, uint8_t slave_addr, uint8_t *buf, uint8_t len)
+{
+       int ret;
+       struct i2c_rdwr_ioctl_data i2c_data;
+       struct i2c_msg msgs[1];
+       int i2c_file = ((struct i2c_info_t *)(gauge->interface))->i2c_file;
+
+       msgs[0].addr = slave_addr >> 1;
+       /* reg address is part of buf */
+       msgs[0].buf = buf;
+       msgs[0].flags = 0;
+       msgs[0].len = len + 1;
+
+       i2c_data.nmsgs = 1;
+       i2c_data.msgs = msgs;
+       ret = ioctl(i2c_file, I2C_RDWR, &i2c_data);
+       if (ret < 0) {
+               fprintf(stderr, "I2C write failed slave %02x reg %02x len %d err %d\n",
+                       msgs[0].addr, buf[0], len, ret);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int read_bq_poll_intvl(void)
+{
+       int poll_file = -1;
+       int poll_intvl = -1;
+       char buf[20];
+
+       poll_file = open(SYSFS_POLL_INTVL, O_RDONLY);
+
+       if ((poll_file >= 0) && read(poll_file, buf, 20)) {
+               sscanf(buf, "%d", &poll_intvl);
+               pr_dbg("gauge driver poll interval %ds\n", poll_intvl);
+       } else {
+               pr_err("Failed to read %s\n", SYSFS_POLL_INTVL);
+       }
+
+       if (poll_file >= 0)
+               close(poll_file);
+
+       return poll_intvl;
+}
+
+static int write_bq_poll_intvl(int poll_intvl)
+{
+       int poll_file = -1;
+       char buf[20];
+       int old_poll_intvl, new_poll_intvl;
+
+       old_poll_intvl = read_bq_poll_intvl();
+
+       if (old_poll_intvl == poll_intvl) {
+               pr_info("polling interval already %ds\n", poll_intvl);
+               return 1;
+       }
+
+
+       poll_file = open(SYSFS_POLL_INTVL, O_RDWR);
+
+       if (poll_file >= 0) {
+               sprintf(buf, "%d", poll_intvl);
+               write(poll_file, buf, 20);
+               close(poll_file);
+       }
+
+       new_poll_intvl = read_bq_poll_intvl();
+
+       if ((poll_file < 0) || (new_poll_intvl != poll_intvl)) {
+               pr_err("failed to set gauge driver polling intvl to %d\n", poll_intvl);
+               return 0;
+       } else {
+               pr_info("changed polling interval from %ds to %ds\n",
+                       old_poll_intvl, poll_intvl);
+               return 1;
+       }
+}
+
+static int lock_gauge_interface_linux(struct gauge_info_t *gauge)
+{
+       struct i2c_info_t *i2c = gauge->interface;
+
+       pr_dbg(">>\n");
+       i2c->drvr_poll_intvl = read_bq_poll_intvl();
+
+       /*
+        * Turn off polling. This may not be enough to silence the
+        * driver. We may have to expose a new sysfs file to completely
+        * lock the driver out from accessing the gauge.
+        */
+       if ((i2c->drvr_poll_intvl > 0) && !write_bq_poll_intvl(0)) {
+               pr_err("Failed to stop driver polling, may cause"
+                       " communication errors during command execution..\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int unlock_gauge_interface_linux(struct gauge_info_t *gauge)
+{
+       struct i2c_info_t *i2c = gauge->interface;
+
+       /* Turn ON polling */
+       if ((i2c->drvr_poll_intvl > 0) &&
+               !write_bq_poll_intvl(i2c->drvr_poll_intvl)) {
+               pr_err("Failed to restore driver polling\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int init_linux_i2c_interface(struct gauge_info_t *gauge, int argc, char **argv)
+{
+       const char *i2c_dev_file, *i2c_bus, *slave_addr;
+       char *tmp;
+       int ret = 0;
+       char buf[100];
+       struct i2c_info_t *i2c;
+
+       pr_dbg(">>\n");
+
+       i2c = (struct i2c_info_t *) malloc(sizeof(struct i2c_info_t));
+       if (!i2c)
+               goto end;
+
+       i2c_dev_file = get_cmdline_argument(argc, argv, "--i2c-dev-file=", 1);
+       if (!i2c_dev_file) {
+               i2c_bus = get_cmdline_argument(argc, argv, "--i2c-bus=", 1);
+               if (i2c_bus) {
+                       snprintf(buf, 100, "%s%s", I2C_DEV_FILE_BASE, i2c_bus);
+               } else {
+                       snprintf(buf, 100, "%s%d", I2C_DEV_FILE_BASE, I2C_BUS);
+                       pr_err("I2C dev file not specified - assuming %s\n", buf);
+               }
+               i2c_dev_file = buf;
+       }
+
+
+       i2c->i2c_file = open(i2c_dev_file, O_RDWR);
+       if (i2c->i2c_file < 0) {
+               pr_err("Failed to open I2C device %s\n", i2c_dev_file);
+               goto end;
+       }
+
+       slave_addr = get_cmdline_argument(argc, argv, "--slave-addr=", 1);
+       if (slave_addr) {
+               gauge->slave_addr = (uint8_t) strtoul(slave_addr, &tmp, 16);
+       } else {
+               pr_err("slave address not provided, assuming 0x%02x\n",
+                       I2C_SLAVE_ADDR_DEFUALT);
+               gauge->slave_addr = I2C_SLAVE_ADDR_DEFUALT;
+       }
+
+       gauge->interface = i2c;
+       ret = 1;
+end:
+       if (!ret)
+               pr_err("failed to initialize I2C interface\n");
+
+       pr_dbg("<<\n");
+       return ret;
+}
+
+static void close_linux_i2c_interface(struct gauge_info_t *gauge)
+{
+       struct i2c_info_t *i2c = gauge->interface;
+
+       close(i2c->i2c_file);
+
+       free(i2c);
+
+       gauge->interface = NULL;
+}
+
+static void sleep_ms(uint16_t ms)
+{
+       usleep(ms * 1000);
+}
+
+int setup_comm_callbacks(struct gauge_info_t *gauge)
+{
+       gauge->init_comm_interface = init_linux_i2c_interface;
+       gauge->close_comm_interface = close_linux_i2c_interface;
+       gauge->lock_gauge_interface = lock_gauge_interface_linux;
+       gauge->unlock_gauge_interface = unlock_gauge_interface_linux;
+       gauge->read = linux_i2c_read;
+       gauge->write = linux_i2c_write;
+       gauge->sleep_ms = sleep_ms;
+
+       return 1;
+}
+
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..e309069
--- /dev/null
+++ b/main.c
@@ -0,0 +1,2389 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Inc
+ *
+ * Aneesh V <aneesh@ti.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <malloc.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "bqt.h"
+
+#define ARRAY_SIZE(arr) (sizeof((arr))/sizeof(arr[0]))
+/*
+ * Sample CSV Header:
+* Texas Instruments Data Flash File
+* File created Fri Oct 02 17:52:22 2015
+*
+* Device Number 421
+* Firmware Version 1.08.10
+* Build Number not available
+* Order Number not available
+*
+* bqz Device Number 0x0421
+* bqz Firmware Version 0x0108
+* bqz Build Number 1.08
+*
+* Field Order: Class name, SubClass name, Parameter name, Parameter Value, Data Type, Data Length, Address Offset, Default Value, Minimum Value, Maximum Value, Display Format, Read Formula, Write Formula, Flags, Display Units, Help, Native Units, Data Flash Raw Value
+ */
+
+const char *fld_ordr_marker = "* Field Order:";
+const char *dev_num_marker = "* Device Number ";
+const char *fw_ver_marker = "* Firmware Version ";
+const char *disp_unit_fld_name = "Display Units";
+
+struct command_t {
+       const char *cmd_name;
+       int (*cmd_func)(int argc, char **argv);
+       const char *help;
+};
+
+struct export_format_t {
+       char **fields;
+       int     num_fields;
+       uint8_t value_fld_ind;
+       uint8_t *fmt_csv_to_params_csv_fld_map;
+};
+
+
+struct export_info_t {
+       FILE            *fd;
+       uint32_t        period;
+       uint8_t         no_hdr;
+       uint8_t         timestamp;
+};
+
+enum cmd_type_t {
+       CMD_INVALID = 0,
+       CMD_R,  /* Read */
+       CMD_W,  /* Write */
+       CMD_C,  /* Compare */
+       CMD_X,  /* Delay */
+};
+
+enum bqfs_mode_t {
+       BQFS_HDQ = 1,
+       BQFS_I2C
+};
+
+struct bqfs_cmd_t {
+       char cmd;
+       uint8_t mode;
+       uint8_t addr;
+       uint8_t len;
+       uint16_t delay;
+       /* Maximum length including slave address, register, and data */
+       uint8_t max_len;
+       uint32_t line_num;
+       uint8_t *buf;
+       uint8_t *tmp_buf;
+};
+
+void print(FILE *out, const char *fmt, ...)
+{
+        va_list ap;
+
+        va_start(ap, fmt);
+        vfprintf(out, fmt, ap);
+        va_end(ap);
+}
+
+/*
+ * Field names for fields used in this program.
+ * They must be strictly int order of the fields in
+ * enum field_t
+ */
+static const char *used_fields[NUM_USED_FIELDS] = {
+       "Parameter name",       /* USED_FLD_NAME */
+       "Parameter Value",      /* USED_FLD_VALUE */
+       "Minimum Value",        /* USED_FLD_MIN_VAL */
+       "Maximum Value",        /* USED_FLD_MAX_VAL */
+       "Data Type",            /* USED_FLD_DATATYPE */
+       "Data Length",          /* USED_FLD_DATA_LENGTH */
+       "Address Offset",       /* USED_FLD_ADDRESS_OFFST */
+       "Display Format",       /* USED_FLD_DISPLAY_FORMAT */
+       "Read Formula",         /* USED_FLD_READ_FORMULA */
+       "Write Formula",        /* USED_FLD_WRITE_FORMULA */
+};
+
+
+/*
+ * If str2 is part of str1 return the remaining
+ * part of str1 after str2.
+ * E.g.:
+ * str1 = "* Device Number 0x0545"
+ * str2 = "Device Number "
+ * strstr_end output: "0x0545"
+ */
+static char *strstr_end(char *str1, const char *str2)
+{
+       char *res = strstr(str1, str2);
+
+       if (res)
+               res += strlen(str2);
+
+       return res;
+}
+
+static char *skip_leading_whitespaces(char *str)
+{
+       char *c = str;
+
+       while((*c == ' ' || *c == '\t') && (*c != '\0'))
+               c++;
+
+       return c;
+}
+
+char *read_line(struct file_info_t *file)
+{
+       int c;
+       int i = 0;
+       FILE *fd = file->fd;
+       char *line_buf = file->line_buf;
+
+       file->line_num++;
+
+       while (1) {
+               c = fgetc(fd);
+
+               if (feof(fd)) {
+                       pr_dbg("EOF\n");
+                       break;
+               } else if (ferror(fd)) {
+                       i = 0;
+                       break;
+               }
+
+               if (((c == '\r') || (c == '\n') || (c == '\t')
+                       || (c == ' ')) && (i == 0)) {
+                       /*
+                        * Skip leading white space, if any, at the beginning
+                        * of the line because this interferes with strtok
+                        */
+                       pr_dbg("Leading whitespace at line %d\n",
+                               file->line_num);
+                       if (c == '\n')
+                               file->line_num++;
+                       continue;       /* blank line, let's continue */
+               } else if (c == '\r' || c== '\n') {
+                       /* We've reached end of line */
+                       break;
+               }
+
+               line_buf[i++] = c;
+
+               if (i == file->max_line_len) {
+                       /*
+                        * Re-allocate in case the line is longer than
+                        * expected
+                        */
+                       line_buf = (char *)realloc(line_buf, file->max_line_len * 2);
+                       pr_err("Line %d longer than expected,"
+                               " reallocating..\n", file->line_num);
+               } else if (i == file->max_line_len * 2) {
+                       /*
+                        * The line is already twice the expected maximum length
+                        * - maybe the bqfs/dffs needs to be fixed
+                        */
+                       pr_err("Line %d too long, abort parsing..\n",
+                               file->line_num);
+                       i = 0;
+                       break;
+               }
+       }
+
+       line_buf[i++] = '\0';
+
+       if (i < 2)
+               return NULL;
+
+       return line_buf;
+}
+
+/*
+ * Get tokens from a csv. Comma at the end results in an
+ * empty string as the last token. Successive commas also
+ * result in empty string as token.
+ */
+static char *get_delim_separated_item(char **str, char delim)
+{
+       char *start = *str;
+       char *c = *str;
+
+       if (!start)
+               return NULL;
+
+       c = start = skip_leading_whitespaces(start);
+
+       while (*c != delim && *c != '\0')
+               c++;
+
+       if (*c == delim) {
+               *str = c + 1;
+               /* ',' found now, strip trailing whitespace if any */
+               while(*(--c) == ' ' || *c == '\t' || *c == '\n')
+                       pr_err("whitespace found in csv field: %s\n", start);
+               /* Null terminate */
+               *(++c) = '\0';
+       } else {
+               *str = NULL;
+       }
+
+       return start;
+}
+
+static uint8_t extract_csv_fields(char *line, char ***fieldsp,
+       uint8_t max_fields)
+{
+       const char *field;
+       int tkn_num = 0, tkn_len;
+       char **fields = *fieldsp;
+
+       fields = (char **) malloc(sizeof(char*) * max_fields);
+       memset(fields, 0, sizeof(char*) * max_fields);
+
+       while ((field = get_delim_separated_item(&line, ',')) && (tkn_num < max_fields)) {
+
+               tkn_len = strlen(field);
+               fields[tkn_num] = (char *) malloc(tkn_len + 1);
+               /* copy the string from line to newly allocated string */
+               memcpy(fields[tkn_num], field, tkn_len + 1);
+               tkn_num++;
+       }
+
+       /*
+        * Now that we know the number of fields, allocate a new array for
+        * fields with just the right number of entries
+        */
+       if (tkn_num && tkn_num < max_fields) {
+               fields = realloc(fields, sizeof(char*) * tkn_num);
+       } else if (!tkn_num) {
+               free(fields);
+               fields = NULL;
+       }
+
+       *fieldsp = fields;
+
+       return tkn_num;
+}
+
+static int strcicmp(char const *a, char const *b)
+{
+    for (;; a++, b++) {
+        int d = tolower(*a) - tolower(*b);
+        if (d != 0 || !*a || !*b)
+            return d;
+    }
+}
+
+static int get_field_map(uint8_t *A_to_B_map, char **fld_arr_A, int len_A, char **fld_arr_B, int len_B)
+{
+       int i, j;
+
+       for (i = 0; i < len_A; i++) {
+               /*
+                * Some of the shorter versions of the gg.csv files
+                * out there have "Units" instead of "Display Units"
+                * Be forgiving with such files
+                */
+               if (strcicmp(fld_arr_A[i], "Units") == 0) {
+                       fld_arr_A[i] = (char *) realloc(fld_arr_A[i],
+                               strlen(disp_unit_fld_name) + 1);
+                       if (!fld_arr_A[i])
+                               return 0;
+                       strcpy(fld_arr_A[i], disp_unit_fld_name);
+               }
+
+               for (j = 0; j < len_B; j++) {
+                       if (strcicmp(fld_arr_A[i], fld_arr_B[j]) == 0) {
+                               A_to_B_map[i] = j;
+                               pr_dbg("field \"%s\" at index %d\n", fld_arr_A[i], j);
+                               break;
+                       }
+               }
+
+               if (j == len_B) {
+                       pr_err("couldn't find field :%s:\n", fld_arr_A[i]);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int parse_header(struct csv_header_t *header)
+{
+       bool found_fld_ordr = 0;
+       bool found_device = 0;
+       bool found_fw_ver = 0;
+       char *line = NULL;
+       char *token = NULL, *tmp;
+       uint32_t fw_version = 0;
+       int i = 0;
+
+       header->num_fields = 0;
+
+       /*
+        * On finding the field order marker, don't go any further looking
+        * for header. Other than that no assumptions about the location
+        * of specific header items.
+        */
+       while (!found_fld_ordr && (line = read_line(header->file))) {
+               if ((token = strstr_end(line, fld_ordr_marker)))
+               {
+                       found_fld_ordr = 1;
+
+                       header->num_fields = extract_csv_fields(token, &header->field_names,
+                               MAX_CSV_FIELDS);
+               } else if ((token = strstr_end(line, dev_num_marker))) {
+                       found_device = 1;
+                       header->device_num = (uint16_t)strtoul(token, NULL, 16);
+                       pr_dbg("device_num = %d\n", header->device_num);
+               } else if ((token = strstr_end(line, fw_ver_marker))) {
+                       i = 0;
+                       while ((tmp = get_delim_separated_item(&token, '.'))) {
+                               fw_version <<= 8;
+                               fw_version |= (uint8_t) strtoul(tmp, NULL, 16);
+                               i++;
+                       }
+
+                       found_fw_ver = 1;
+                       if (i == 2) {
+                               header->fw_version = (uint16_t) fw_version;
+                       } else if (i == 3) {
+                               header->fw_version = (uint16_t) (fw_version >> 8);
+                               header->fw_ver_bld = (uint16_t) (fw_version & 0xFF);
+                       } else {
+                               found_fw_ver = 0;
+                               continue;
+                       }
+               }
+       }
+
+       pr_dbg("device = 0x%04x fw = 0x%04x num_fields = %d\n",
+               header->device_num,
+               header->fw_version, header->num_fields);
+
+       if (!found_device || !found_fw_ver) {
+               pr_err("could not find dev info from hdr dev=0x%04x fw=0x%04x\n",
+                       header->device_num, header->fw_version);
+       }
+
+       if (!header->num_fields){
+               pr_err("could not find field info from header\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int discard_unused_flds(char ***fields, int num_used_flds, uint8_t *used_flds_map)
+{
+       char **tmp = (char **) malloc(sizeof(char *) * num_used_flds);
+       int i;
+
+       pr_dbg(">> %x %d %x\n", fields, num_used_flds, used_flds_map);
+
+       if (!tmp) {
+               pr_err("failed\n");
+               return 0;
+       }
+
+       for (i = 0; i < num_used_flds; i++)
+               tmp[i] = (*fields)[used_flds_map[i]];
+
+       free(*fields);
+       *fields = tmp;
+
+       pr_dbg("<<\n");
+
+       return 1;
+}
+
+/*
+ * Use hard-coded format strings
+ * We don't want to allocate one format string per parameter,
+ * so just point to string literals
+ */
+const char *get_float_fmt_str(int fract_len)
+{
+       const char *fmt;
+
+       switch (fract_len) {
+       case 1:
+               fmt = "%.1f";
+               break;
+       case 2:
+               fmt = "%.2f";
+               break;
+       case 3:
+               fmt = "%.3f";
+               break;
+       case 4:
+       default:
+               fmt = "%.4f";
+               break;
+       }
+
+       return fmt;
+}
+
+/*
+ * Use hard-coded format strings
+ * We don't want to allocate one format string per parameter,
+ * so just point to string literals
+ */
+const char *get_hex_fmt_str(int len)
+{
+       const char *fmt;
+
+       switch (len) {
+       case 2:
+               fmt = "%02x";
+               break;
+       case 4:
+               fmt = "%04x";
+               break;
+       case 8:
+       default:
+               fmt = "%08x";
+               break;
+       }
+
+       return fmt;
+}
+
+/*
+ * Display format affects how the parameter is read from
+ * and written to the csv. For instance an integer data
+ * type with display format dd.dd will be read from the csv
+ * as a float and multiplied by 100 before applying the
+ * write formula and writing to DM (divide by 100 in the
+ * opposite direction).
+ */
+static int get_display_format(const char *str, struct param_t *param)
+{
+       int fract_len = 0, len = 0, num_dec_pts = 0;
+       const char *dec_point = NULL;
+       const char *c = str;
+       char type_char, ch;
+
+       /* Move until we find the type character */
+       while (*c++ == '.');
+
+       type_char = *(--c);
+       type_char = tolower(type_char);
+
+       /* String is a special case, no need to look for decimal points etc */
+       if (type_char == 's') {
+               param->disp_type = DISP_FMT_S;
+               param->csv_fraction_len = 0;
+               param->fmt_str = "%s";
+               return 1;
+       }
+
+
+       c = str;
+
+       while ((ch = *c)) {
+               if (ch == '.') {
+                       num_dec_pts++;
+                       dec_point = c;
+               } else if (ch != type_char) {
+                       pr_err("multiple type characters in display"
+                               " format: %s", str);
+                       return 0;
+               }
+               c++;
+               len++;
+       }
+
+       if (!len || num_dec_pts > 1) {
+               pr_err("wrong display format \"%s\": len=%d num_dec_pts=%d\n",
+                       str, len, num_dec_pts);
+               return 0;
+       }
+
+       if (num_dec_pts == 1)
+               fract_len = len - (dec_point - str) - 1;
+       else
+               fract_len = 0;
+
+       switch (type_char) {
+       case 'd':
+               param->csv_fraction_len = fract_len;
+               if (!fract_len) {
+                       param->disp_type = DISP_FMT_I;
+                       param->fmt_str = "%lld";
+               } else {
+                       /*
+                        * Although the DM value is integer it's
+                        * converted to float for displaying in csv
+                        */
+                       param->disp_type = DISP_FMT_F;
+                       param->fmt_str = get_float_fmt_str(fract_len);
+               }
+               break;
+       case 'h':
+               param->disp_type = DISP_FMT_H;
+               if (fract_len) {
+                       pr_err("fractional part not supported for hex display format\n");
+                       return 0;
+               }
+               param->csv_fraction_len = 0;
+               param->fmt_str = get_hex_fmt_str(len);
+               break;
+       case 'f':
+               param->disp_type = DISP_FMT_F;
+               param->csv_fraction_len = 0;
+               param->fmt_str = get_float_fmt_str(fract_len);
+               break;
+       case 's':
+               /* Already taken care of, we shouldn't be here, so fall through */
+       default:
+               pr_err("Unsupported display format \"%s\"\n", str);
+               return 0;
+
+       }
+
+
+       return 1;
+}
+
+int extract_int(const char *str, int base, long long int *res)
+{
+       char *temp;
+
+       *res = strtoll(str, &temp, base);
+
+       if (str == temp) {
+               pr_err("extracting integer failed: %s\n", str);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int extract_integer(const char *str, long long int *res)
+{
+       if ((strstr(str, "0x") == str) || (strstr(str, "0X") == str))
+               return extract_int(str, 16, res);
+       else
+               return extract_int(str, 10, res);
+}
+
+static int extract_double(const char *str, double *res)
+{
+       char *temp;
+
+       *res = strtod(str, &temp);
+
+       if (str == temp) {
+               pr_err("Extracting double failed: %s\n", str);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * Get the scale factor:
+ * Scale factor is the inverse of division factor
+ * and division factor is saved as log10 value.
+ */
+static int get_scale_factor(int div_factor_log)
+{
+       int scale_factor = 1, i;
+       for (i = 0; i < div_factor_log; i++)
+               scale_factor *= 10;
+
+       return scale_factor;
+}
+
+static long long int sign_extend(int32_t val, int len)
+{
+       long long int ll;
+
+       switch (len) {
+       case 1:
+               ll = (int8_t) (val & 0xFF);
+               break;
+       case 2:
+               ll = (int16_t) (val & 0xFFFF);
+               break;
+       case 4:
+       default:
+               ll = (int32_t) (val & 0xFFFFFFFF);
+               break;
+       }
+
+       return ll;
+}
+
+int check_limits(struct param_t *param)
+{
+       int8_t ovflow = 0, uflow = 0;
+       long long int val, min, max;
+
+       switch (param->raw_type) {
+       case DATATYPE_I:
+               val = sign_extend(param->val.i, param->data_len);
+               min = sign_extend(param->min.i, param->data_len);
+               max = sign_extend(param->max.i, param->data_len);
+               ovflow = val > max;
+               uflow = val < min;
+               break;
+       case DATATYPE_U:
+               ovflow = param->val.u > param->max.u;
+               uflow = param->val.u < param->min.u;
+               break;
+       case DATATYPE_F:
+               ovflow = param->val.f > param->max.f;
+               uflow = param->val.f < param->min.f;
+               break;
+       case DATATYPE_S:
+               /* ignore limits */
+               break;
+       default:
+               return 0;
+       }
+
+       if (ovflow) {
+               pr_err("%s: parameter value exceeds max value"
+                       " 0x%04x 0x%04x\n", param->name, param->val.u,
+                       param->max.u);
+       }
+
+       if (uflow) {
+               pr_err("%s: parameter value less than min value"
+                       " 0x%04x 0x%0x4x\n", param->name, param->val.u,
+                       param->min.u);
+       }
+
+       return !(ovflow || uflow);
+}
+
+int update_value_string(struct param_t *param, int value_fld_ind)
+{
+       unsigned int scale_factor, len;
+       long long int ll;
+       char *str;
+       char new_str[MAX_USED_FLD_LEN];
+       double d;
+
+
+       if (param->raw_type == DATATYPE_S) {
+               snprintf(new_str, param->data_len - 1, "%s", &param->val_s[1]);
+               goto update_str;
+       }
+
+       scale_factor = get_scale_factor(param->csv_fraction_len);
+       switch (param->raw_type) {
+       case DATATYPE_I:
+               ll = sign_extend(param->val.i, param->data_len);
+               d = ll;
+               break;
+       case DATATYPE_U:
+               /*
+                * Assumption is that long long is at least 64
+                * bits long. This seems to be guaranteed by C99
+                * https://en.wikipedia.org/wiki/C_data_types#Size
+                */
+               ll = (long long int) param->val.u;
+               d = ll;
+               pr_dbg("u %u ll %ld d %lg", param->val.u, ll, d);
+               break;
+       case DATATYPE_F:
+       default:
+               d = param->val.f;
+               break;
+       }
+
+       if (param->read_formula_expr) {
+               d = evaluate_expr(param->read_formula_expr, d);
+               ll = (long long int) d;
+       }
+
+       if (scale_factor != 1)
+               d /= scale_factor;
+
+       if (param->disp_type == DISP_FMT_F)
+               sprintf(new_str, param->fmt_str, (float) d);
+       else
+               sprintf(new_str, param->fmt_str, ll);
+
+update_str:
+       str = param->fields[value_fld_ind];
+       len = strlen(new_str);
+       if (len > strlen(str)) {
+               str = (char *) realloc(str, len + 1);
+               if (!str)
+                       return 0;
+       }
+       memcpy(str, new_str, len + 1);
+       param->fields[value_fld_ind] = str;
+
+       pr_dbg("%s %s\n", str, new_str);
+
+       return 1;
+}
+/*
+ * The "x" in read/write formula is the raw DF value.
+ * So, the display format(like dd.dd) related scaling
+ * should be applied before applying the formula in the
+ * write direction and the scaling should be applied after
+ * the formula in read direction.
+ *
+ * The min, max limit check is applicable only in write
+ * direction and applies after all transformations. So, they
+ * are in the same unit as the raw DF value.
+ */
+
+static int extract_param_data(struct param_t *param, uint8_t *used_fld_ind)
+{
+       int err, scale_factor, base;
+       unsigned long int ul;
+       long long int llmax, llmin, llval;
+       double d;
+       char *str, *tmp, *val, *min, *max, *name;
+       unsigned int len;
+
+       /*
+        * Parameter name
+        */
+       name = param->fields[used_fld_ind[USED_FLD_NAME]];
+
+       /*
+        * Address Offset
+        */
+       str = param->fields[used_fld_ind[USED_FLD_ADDRESS_OFFST]];
+       ul = strtoul(str, &tmp, 16);
+       if (str == tmp) {
+               pr_err("%s: couldn't extract param offset: %s\n", name, str)
+               return 0;
+       }
+       /* The MSB byte is subclass and the LSB byte is offset */
+       param->offset = (uint32_t) ul & 0xFFFFFFFF;
+
+       /*
+        * Data Length
+        */
+       str = param->fields[used_fld_ind[USED_FLD_DATA_LENGTH]];
+       err = extract_int(str, 10, &llval);
+       if (!err) {
+               pr_err("%s: error parsing datalength \"%s\"\n", name, str);
+               return 0;
+       }
+       param->data_len = (uint8_t) llval;
+
+       /*
+        * Read Formula, Write Formula
+        */
+       str = param->fields[used_fld_ind[USED_FLD_READ_FORMULA]];
+       /* No need to create Reverse Polish Notation expression if formula is "x" */
+       if (strcmp(str, "x") != 0 && strcmp(str, "X") != 0) {
+               //printf("%s\n", str);
+               param->read_formula_expr = parse_expression(str);
+               if (!param->read_formula_expr) {
+                       pr_err("%s: Error parsing read formula \"%s\"\n", name, str);
+                       return 0;
+               }
+
+       }
+       str = param->fields[used_fld_ind[USED_FLD_WRITE_FORMULA]];
+       /* No need to create Reverse Polish Notation expression if formula is "x" */
+       if (strcmp(str, "x") != 0 && strcmp(str, "X") != 0) {
+               param->write_formula_expr = parse_expression(str);
+               if (!param->write_formula_expr) {
+                       pr_err("%s: Error parsing write formula \"%s\"\n", name, str);
+                       return 0;
+               }
+       }
+
+       /*
+        * Display Format
+        */
+       str = param->fields[used_fld_ind[USED_FLD_DISPLAY_FORMAT]];
+       if (!get_display_format(str, param))
+               return 0;
+
+       /*
+        * Data Type
+        */
+       str = param->fields[used_fld_ind[USED_FLD_DATATYPE]];
+       switch (toupper(*str)) { /* Assume datatype to be single char */
+       case 'I':
+               param->raw_type = DATATYPE_I;
+               break;
+       case 'U':
+       case 'B':
+               param->raw_type = DATATYPE_U;
+               break;
+       case 'F':
+               param->raw_type = DATATYPE_F;
+               break;
+       case 'S':
+               param->raw_type = DATATYPE_S;
+               if (param->disp_type != DISP_FMT_S)
+                       return 0;
+               break;
+       default:
+               pr_err("%s: unknown data type %s", name, str);
+               return 0;
+       }
+
+       /*
+        * Min and Max Values
+        */
+       min = param->fields[used_fld_ind[USED_FLD_MIN_VAL]];
+       max = param->fields[used_fld_ind[USED_FLD_MAX_VAL]];
+       if (param->disp_type == DISP_FMT_H)
+               base = 16;
+       else
+               base = 10;
+
+       err = 0;
+       switch(param->raw_type) {
+       case DATATYPE_I:
+       case DATATYPE_U:
+               err |= !extract_int(min, base, &llmin);
+               err |= !extract_int(max, base, &llmax);
+               if (param->raw_type == DATATYPE_I) {
+                       param->min.i = (int32_t) (llmin & 0xFFFFFFFF);
+                       param->max.i = (int32_t) (llmax & 0xFFFFFFFF);
+
+               } else {
+                       param->min.u = (uint32_t) (llmin & 0xFFFFFFFF);
+                       param->max.u = (uint32_t) (llmax & 0xFFFFFFFF);
+               }
+               break;
+       case DATATYPE_F:
+               err |= !extract_double(min, &d);
+               param->max.f = (float) d;
+               err |= !extract_double(max, &d);
+               param->max.f = (float) d;
+               break;
+       case DATATYPE_S:
+               /* In case of strings just ignore min and max */
+               break;
+       default:
+               return 0;
+       }
+
+       val = param->fields[used_fld_ind[USED_FLD_VALUE]];
+       switch(param->disp_type) {
+       case DISP_FMT_I:
+       case DISP_FMT_H:
+               err |= !extract_int(val, base, &llval);
+               if (param->raw_type == DATATYPE_I) {
+                       param->val.i = (int32_t) (llval & 0xFFFFFFFF);
+                       d = param->val.i;
+               } else {
+                       param->val.u = (uint32_t) (llval & 0xFFFFFFFF);
+                       d = param->val.u;
+               }
+               break;
+       case DISP_FMT_F:
+               /*
+                * Parameters with display formats like dd.d also
+                * will end up here
+                */
+               err |= !extract_double(val, &d);
+               break;
+       case DISP_FMT_S:
+               if (param->data_len > 32) {
+                       pr_err("%s: bqtool doesn't support a string longer than 31"
+                               " bytes long. Consider fixing bqtool!!\n", name);
+                       return 0;
+               }
+               /*
+                * Please note that per the spec, the first byte in the buffer is the length,
+                * rest the string itself. The string need not be null terminated, but let's
+                * null terminate it for use within the tool
+                */
+               param->val_s = (char *) malloc(param->data_len + 1);
+               if (!param->val_s)
+                       return 0;
+               memset(param->val_s, 0, param->data_len + 1);
+               len = strlen(val);
+               if (len > (unsigned) (param->data_len - 1))
+                       len = param->data_len - 1;
+               strncpy(&param->val_s[1], val, len);
+               param->val_s[0] = (uint8_t) len;
+               param->val_s[len + 1] = '\0';
+               break;
+       default:
+               return 0;
+       }
+
+       if (err) {
+               pr_err("%s: error extracting Parameter Value\n", name);
+               return 0;
+       }
+
+       scale_factor = get_scale_factor(param->csv_fraction_len);
+       if (scale_factor != 1)
+               d *= scale_factor;
+       if (param->write_formula_expr)
+               d = evaluate_expr(param->write_formula_expr, d);
+
+       switch(param->raw_type) {
+       case DATATYPE_I:
+               param->val.i = (int32_t) d;
+               break;
+       case DATATYPE_U:
+               param->val.u = (uint32_t) d;
+               break;
+       case DATATYPE_F:
+               param->val.f = (float) d;
+               break;
+       }
+
+       return 1;
+}
+
+static int copy_string(char **dest, const char *src)
+{
+       unsigned int len = strlen(src);
+
+       *dest = (char *) malloc(len + 1);
+       if (!(*dest))
+               return 0;
+
+       strcpy(*dest, src);
+
+       return 1;
+}
+
+static int parse_params(struct csv_info_t *csv, struct export_format_t *exp)
+{
+       int num_fields, i = 0, err = 0;
+       char *line;
+       struct file_info_t *file = csv->header->file;
+       const char *name;
+
+       pr_dbg(">>\n");
+
+       struct param_t *params = (struct param_t *) malloc(sizeof(struct param_t) * MAX_NUM_PARAMS);
+       if (!params)
+               return 0;
+
+       memset(params, 0, sizeof(struct param_t) * MAX_NUM_PARAMS);
+       csv->params = params;
+
+       while ((line = read_line(file)) && (i < MAX_NUM_PARAMS) && !err) {
+               num_fields = extract_csv_fields(line, &params[i].fields, csv->header->num_fields);
+               if (num_fields != csv->header->num_fields) {
+                       pr_err("parameter parsing failed 1 at \"%s\":%d num_fields = %d\n",
+                               file->fname, file->line_num, num_fields);
+                       return 0;
+               }
+
+               if (!extract_param_data(&params[i], csv->used_field_index))
+                       return 0;
+
+               name = params[i].fields[csv->used_field_index[USED_FLD_NAME]];
+               if (!copy_string(&params[i].name, name))
+                       return 0;
+               /*
+                * Now that parsing the parameter is over, remove fields
+                * that are not needed for export if user has provided
+                * export format
+                */
+               if (exp) {
+                       if (!discard_unused_flds(&params[i].fields, exp->num_fields,
+                                       exp->fmt_csv_to_params_csv_fld_map))
+                               return 0;
+               }
+
+               i++;
+       }
+
+       if (i == 0) {
+               pr_err("No parameters in csv\n");
+               return 0;
+       }
+       csv->num_params = i;
+
+       if (line) {
+               pr_err("parameter parsing failed - more parameters than expected\"%s: %d\n",
+                               file->line_num);
+               return 0;
+       }
+
+       /* reallocate the array to save space */
+       if (csv->num_params < MAX_NUM_PARAMS)
+               csv->params = realloc(params, sizeof(struct param_t) * csv->num_params);
+
+       if (!csv->params)
+               return 0;
+
+       pr_dbg("<<\n");
+
+       return 1;
+}
+
+struct gauge_info_t *create_gauge(void)
+{
+       struct gauge_info_t *gauge =
+               (struct gauge_info_t *) malloc(sizeof(struct gauge_info_t));
+
+       if (!gauge)
+               return NULL;
+
+       memset(gauge, 0, sizeof(struct gauge_info_t));
+
+
+       return gauge;
+}
+
+static void free_gauge(struct gauge_info_t *gauge)
+{
+       if (!gauge)
+               return;
+
+#if 0
+       if (gauge->unlock_gauge_interface)
+               gauge->unlock_gauge_interface(gauge);
+#endif
+
+       if (gauge->close_comm_interface)
+               gauge->close_comm_interface(gauge);
+
+       if (gauge->interface)
+               free(gauge->interface);
+
+       free(gauge);
+}
+
+static void free_fields(char **fields, int num_fields)
+{
+       int i;
+
+       if (!fields)
+               return;
+
+       for (i = 0; i < num_fields; i++) {
+               if (fields[i])
+                       free(fields[i]);
+       }
+
+       free(fields);
+}
+
+static void free_param(struct param_t *param, int num_fields)
+{
+       if (!param)
+               return;
+
+       if (param->fields)
+               free_fields(param->fields, num_fields);
+
+       if (param->read_formula_expr)
+               delete_queue(param->read_formula_expr);
+
+       if (param->write_formula_expr)
+               delete_queue(param->write_formula_expr);
+
+       if (param->raw_type == DATATYPE_S && param->val_s)
+               free(param->val_s);
+
+       if (param->name)
+               free(param->name);
+}
+
+static void free_params(struct param_t *params, int num_params, int num_fields)
+{
+       int i;
+
+       if (!params)
+               return;
+
+       for (i = 0; i < num_params; i++)
+               free_param(&params[i], num_fields);
+
+       free(params);
+}
+
+static int file_exists(const char *fname)
+{
+
+       FILE *fd = fopen(fname, "r");
+
+       if (!fd)
+               return 0;
+       else {
+               fclose(fd);
+               return 1;
+       }
+
+}
+
+void free_file(struct file_info_t *file)
+{
+       if (!file)
+               return;
+
+       if (file->fd) {
+               fclose(file->fd);
+               file->fd = NULL;
+       }
+
+       if (file->line_buf) {
+               free(file->line_buf);
+               file->line_buf = NULL;
+       }
+
+       free(file);
+}
+
+struct file_info_t *create_file(const char *fname, const char *mode,
+       int max_line_len)
+{
+       struct file_info_t *file =
+               (struct file_info_t *) malloc(sizeof(struct file_info_t));
+
+       if (!file)
+               return NULL;
+
+       file->fd = fopen(fname, mode);
+       if (!file->fd) {
+               pr_err("Error opening file %s\n", fname);
+               free(file);
+               return NULL;
+       }
+
+       file->line_buf = (char *) malloc(max_line_len);
+       if (!file->line_buf) {
+               free(file);
+               return NULL;
+       }
+       file->max_line_len = max_line_len;
+
+       return file;
+}
+
+static void free_csv_hdr(struct csv_header_t *header)
+{
+       if (!header)
+               return;
+
+       if (header->file)
+               free_file(header->file);
+
+       if (header->field_names)
+               free_fields(header->field_names, header->num_fields);
+}
+
+
+
+
+struct csv_header_t *create_csv_hdr(const char *fname)
+{
+       struct csv_header_t *header =
+               (struct csv_header_t *) malloc(sizeof(struct csv_header_t));
+
+       if (!header)
+               return NULL;
+
+       memset(header, 0, sizeof(struct csv_header_t));
+
+       header->file = create_file(fname, "r", MAX_LINE_LEN_GG_CSV);
+       if (!header->file)
+               return NULL;
+
+       return header;
+}
+
+static void free_csv(struct csv_info_t *csv)
+{
+       if (!csv)
+               return;
+
+       if (csv->params)
+               free_params(csv->params, csv->num_params,
+                       csv->header->num_fields);
+
+       if (csv->header)
+               free_csv_hdr(csv->header);
+
+       free(csv);
+}
+
+struct csv_info_t *create_csv(const char *fname)
+{
+       struct csv_info_t *csv = (struct csv_info_t *)
+               malloc(sizeof(struct csv_info_t));
+
+       if (!csv)
+               return NULL;
+
+       memset(csv, 0, sizeof(struct csv_info_t));
+
+       csv->header = create_csv_hdr(fname);
+       if (!csv->header)
+               return NULL;
+
+       return csv;
+}
+
+static void free_exp_fmt(struct export_format_t *exp)
+{
+       if (!exp)
+               return;
+
+       if (exp->fields)
+               free_fields(exp->fields, exp->num_fields);
+
+       if (exp->fmt_csv_to_params_csv_fld_map)
+               free(exp->fmt_csv_to_params_csv_fld_map);
+
+       free(exp);
+}
+
+struct export_format_t *parse_export_format(const char *fname)
+{
+       int i, ret = 0;
+       char *line;
+       struct file_info_t *file = NULL;
+       struct export_format_t *exp = NULL;
+
+       file = create_file(fname, "r", MAX_LINE_LEN_GG_CSV);
+       exp = (struct export_format_t*) malloc(sizeof(struct export_format_t));
+
+       if (!exp || !file)
+               goto end;
+
+       while ((line = read_line(file))) {
+               if ((line = strstr_end(line, fld_ordr_marker)))
+               {
+                       exp->num_fields = extract_csv_fields(line, &exp->fields, MAX_CSV_FIELDS);
+                       break;
+               }
+       }
+
+
+       if (!line || !exp->num_fields)
+               goto end;
+
+       /* Find the index of "Parameter Value" field */
+       for (i = 0; i < exp->num_fields; i++) {
+               if (strcicmp(exp->fields[i], used_fields[USED_FLD_VALUE]) == 0) {
+                       exp->value_fld_ind = i;
+                       break;
+               }
+       }
+
+       if (i == exp->num_fields) {
+               pr_err("couldn't find field \"Paramter Value\" in --format-csv\n");
+               goto end;
+       }
+
+
+       exp->fmt_csv_to_params_csv_fld_map = (uint8_t *) malloc(exp->num_fields);
+       if (!exp->fmt_csv_to_params_csv_fld_map)
+               goto end;
+
+       ret = 1;
+       return exp;
+
+end:
+       free_file(file);
+
+       if (!ret) {
+               free_exp_fmt(exp);
+               return NULL;
+       }
+
+       return exp;
+}
+
+static uint8_t find_key_from_csv(struct csv_info_t *csv, const char *key_name, uint32_t *key)
+{
+       int i;
+       const char *str;
+       char *tmp;
+
+       /*
+        * Keys are typically at the end. So, start from the end
+        * We will do this unconditionally because we want to see
+        * if we will need full access keys.
+        */
+       for (i = csv->num_params - 1; i >= 0; i--) {
+               str = csv->params[i].fields[csv->used_field_index[USED_FLD_NAME]];
+               if (strcicmp(str, key_name) == 0) {
+                       str = csv->params[i].fields[csv->used_field_index[USED_FLD_VALUE]];
+                       *key = strtoul(str, &tmp, 16);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+
+struct csv_info_t *parse_csv_file(const char *fname, struct export_format_t *exp)
+{
+       struct csv_info_t *csv = create_csv(fname);
+       int ret = 1;
+
+       if (!csv)
+               goto error;
+
+       if (!parse_header(csv->header)) {
+               pr_err("error parsing header of csv: %s\n", fname);
+               goto error;
+       }
+
+
+       if (!get_field_map(csv->used_field_index, (char **) used_fields,
+               sizeof(used_fields) / sizeof(used_fields[0]),
+               csv->header->field_names, csv->header->num_fields)) {
+               pr_err("error getting used field indices\n");
+               goto error;
+       }
+       csv->value_fld_ind = csv->used_field_index[USED_FLD_VALUE];
+
+       if (exp) {
+               ret = get_field_map(exp->fmt_csv_to_params_csv_fld_map, exp->fields,
+                               exp->num_fields, csv->header->field_names,
+                               csv->header->num_fields);
+               if (!ret) {
+                       pr_err("Error mapping --format-csv fields to"
+                               " --params-csv fields. Falling back to"
+                               " --params-csv for export format\n")
+                       exp = NULL;
+               }
+               /* Discard unused fields in the header */
+
+       }
+
+       if (exp) {
+               ret = discard_unused_flds(&csv->header->field_names, exp->num_fields,
+                               exp->fmt_csv_to_params_csv_fld_map);
+       }
+
+
+       if (ret && !parse_params(csv, exp)) {
+               pr_err("Error parsing params\n");
+               goto error;
+       }
+
+       if (exp) {
+               csv->header->num_fields = exp->num_fields;
+               csv->value_fld_ind = exp->value_fld_ind;
+       }
+
+       /* See if we can find the unseal keys from the csv */
+       csv->unseal_key_found =
+               find_key_from_csv(csv, "Sealed to Unsealed", &csv->unseal_key);
+       csv->fullaccess_key_found =
+               find_key_from_csv(csv, "Unsealed to Full", &csv->fullaccess_key);
+
+       return csv;
+
+error:
+       free_csv(csv);
+
+       return NULL;
+
+}
+
+static int check_device_match(struct gauge_info_t *gauge, struct csv_info_t *csv)
+{
+       if ( csv->header->device_num != gauge->device_num ||
+               csv->header->fw_version != gauge->fw_version ||
+               (csv->header->fw_ver_bld &&  (csv->header->fw_ver_bld != gauge->fw_ver_bld)))
+       {
+               pr_err("The Device Number or FW version in the gg.csv"
+                       " don't match with respective value read from the"
+                       " device.. CSV: 0x%04x 0x%04x 0x%04x Gauge: 0x%04x"
+                       " 0x%04x 0x%04x\n", csv->header->device_num,
+                       csv->header->fw_version, csv->header->fw_ver_bld,
+                       gauge->device_num, gauge->fw_version, gauge->fw_ver_bld);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * These values will be overridden later by the gauge related code
+ * if we can independently find them from commandline inputs
+ */
+static void init_gauge_from_csv(struct gauge_info_t *gauge, struct csv_info_t *csv)
+{
+       gauge->unseal_key_found = csv->unseal_key_found;
+       gauge->unseal_key = csv->unseal_key;
+       gauge->fullaccess_key_found = csv->fullaccess_key_found;
+       gauge->fullacccess_key = csv->fullaccess_key;
+}
+
+static void print_csv_row(FILE *fd, const char *first_word, char **fields,int num_fields)
+{
+       int i;
+
+       fprintf(fd, "%s", first_word);
+
+       for (i = 0; i < num_fields; i++)
+               fprintf(fd, ",%s", fields[i]);
+
+       fprintf(fd, "\n");
+}
+
+static void print_csv_fld_ordr(FILE *fd, const char *first_word, char **fields,int num_fields)
+{
+       int i;
+
+       fprintf(fd, "%s", first_word);
+
+       for (i = 0; i < num_fields; i++)
+               fprintf(fd, ", %s", fields[i]);
+
+       fprintf(fd, "\n");
+}
+
+static int write_csv_file(struct csv_info_t *csv, struct export_info_t *exp,
+       const char *timestamp)
+{
+       char buf[MAX_USED_FLD_LEN];
+       time_t now;
+       struct tm *t = NULL;
+       uint32_t i;
+       struct param_t *params = csv->params;
+       uint8_t tmp1, tmp2;
+       FILE *fd = exp->fd;
+
+       if (!exp->no_hdr) {
+               now = time(NULL);
+               t = localtime(&now);
+
+               fprintf(fd, "* Texas Instruments Data Flash File\n");
+               fprintf(fd, "* File created %s", asctime(t));
+               fprintf(fd, "* Created by bqtool\n");
+               fprintf(fd, "* Device Number %x\n", csv->header->device_num);
+               tmp1 = (uint8_t) ((csv->header->fw_version >> 8) & 0xFF);
+               tmp2 = (uint8_t) (csv->header->fw_version & 0xFF);
+               fprintf(fd, "* Firmware Version %x.%02x", tmp1, tmp2);
+               if (csv->header->fw_ver_bld)
+                       fprintf(fd, ".%02x\n", csv->header->fw_ver_bld);
+               else
+                       fprintf(fd, "\n");
+
+               fprintf(fd, "* Build Number not available\n");
+               fprintf(fd, "* Order Number not available\n*\n");
+               fprintf(fd, "* bqz Device Number not available\n");
+               fprintf(fd, "* bqz Firmware Version not available\n");
+               fprintf(fd, "* bqz Build Number not available\n*\n");
+               if (timestamp) {
+                       /* First column shall be the timestamp */
+                       snprintf(buf, MAX_USED_FLD_LEN, "%s %s",
+                               fld_ordr_marker, "Timestamp");
+                       print_csv_fld_ordr(fd, buf, csv->header->field_names,
+                               csv->header->num_fields);
+               } else {
+                       snprintf(buf, MAX_USED_FLD_LEN, "%s %s",
+                               fld_ordr_marker, csv->header->field_names[0]);
+                       print_csv_fld_ordr(fd, buf, &csv->header->field_names[1],
+                               csv->header->num_fields - 1);
+               }
+       }
+
+       /* Export all fields */
+       for (i = 0; i < csv->num_params; i++) {
+               if (timestamp) {
+                       print_csv_row(fd, timestamp, params[i].fields,
+                               csv->header->num_fields);
+               } else {
+                       snprintf(buf, MAX_USED_FLD_LEN, "%s",
+                               params[i].fields[0]);
+                       print_csv_row(fd, buf, &params[i].fields[1],
+                               csv->header->num_fields - 1);
+               }
+
+       }
+
+       fflush(fd);
+
+       return !ferror(fd);
+}
+
+const char *get_cmdline_argument(int argc, char **argv, const char *arg_identifier, int arg_with_val)
+{
+       int i;
+       uint32_t len;
+
+       for (i = 2; i < argc; i++) {
+               if (strstr(argv[i], arg_identifier) == argv[i]) {
+                       /*
+                        * For switches such as --export-header
+                        * return the whole string
+                        */
+                       if (!arg_with_val)
+                               return argv[i];
+                       /*
+                        * For arguments such as --params-csv=<csv-file-name>
+                        * return only the <csv-file-name> part
+                        */
+                       len = strlen(arg_identifier);
+                       if (strlen(argv[i]) > len)
+                               return argv[i] + len;
+                       else
+                               return NULL;
+               }
+       }
+
+       pr_dbg("could not find argument \"%s\"\n", arg_identifier);
+
+       return NULL;
+}
+
+struct gauge_info_t *init_gauge(struct csv_info_t *csv, int argc,
+       char **argv, uint8_t op)
+{
+       struct gauge_info_t *gauge = create_gauge();
+       if (!gauge)
+               return NULL;
+       gauge->op = op;
+
+       if ((op == OP_DM_READ || op == OP_DM_WRITE) && csv)
+               init_gauge_from_csv(gauge, csv);
+
+       if (!init_gauge_interface(gauge, argc, argv))
+               goto error;
+
+       if (op == OP_DM_WRITE && !check_device_match(gauge, csv))
+               goto error;
+
+       return gauge;
+error:
+       free_gauge(gauge);
+       return NULL;
+}
+
+static int import_gg_csv(int argc, char **argv)
+{
+       const char *fname;
+       int err = 1;
+       struct gauge_info_t *gauge = NULL;
+       struct csv_info_t *csv = NULL;
+
+       fname = get_cmdline_argument(argc, argv, "--params-csv=", 1);
+       if (!fname) {
+               pr_err("--param-csv not found\n");
+               return 0;
+       }
+
+       csv = parse_csv_file(fname, NULL);
+       if (!csv)
+               goto end;
+
+       gauge = init_gauge(csv, argc, argv, OP_DM_WRITE);
+       if (!gauge)
+               goto end;
+
+       gauge->lock_gauge_interface(gauge);
+
+       if (!gauge->open_dm(gauge, argc, argv))
+               goto end;
+
+       err = !gauge->write_params(csv, gauge);
+
+
+       if (get_cmdline_argument(argc, argv, "--reset", 0)) {
+               gauge->reset(gauge);
+               gauge->sleep_ms(10000);
+       }
+
+       gauge->close_dm(gauge, argc, argv);
+
+end:
+       if (gauge) {
+               gauge->unlock_gauge_interface(gauge);
+               free_gauge(gauge);
+       }
+
+       free_csv(csv);
+
+       return !err;
+}
+
+static void free_exp_info(struct export_info_t *exp)
+{
+       if (!exp)
+               return;
+
+       if (exp->fd && exp->fd != stdout)
+               fclose(exp->fd);
+
+       free_exp_info(exp);
+}
+
+struct export_info_t *get_export_info(int argc, char **argv)
+{
+       const char *no_hdr, *prd, *timestamp, *output_csv;
+       struct export_info_t *exp_info;
+       FILE *fd = stdout;
+
+       exp_info = (struct export_info_t *) malloc(sizeof(struct export_info_t));
+       if (!exp_info)
+               return NULL;
+
+       memset(exp_info, 0, sizeof(struct export_info_t));
+
+       prd = get_cmdline_argument(argc, argv, "--period=", 1);
+       if (prd)
+               exp_info->period = strtoul(prd, NULL, 10);
+
+       timestamp = get_cmdline_argument(argc, argv, "--timestamp", 0);
+       exp_info->timestamp = timestamp ? 1 : 0;
+
+       output_csv = get_cmdline_argument(argc, argv, "--output-csv=", 1);
+       if (output_csv)
+               fd = fopen(output_csv, "w");
+
+       if (!fd) {
+               fd = stdout;
+               pr_err("error opening output csv %s redirecting"
+                       " to stdout instead", output_csv);
+       }
+       exp_info->fd = fd;
+
+       no_hdr = get_cmdline_argument(argc, argv, "--no-header", 0);
+       exp_info->no_hdr = no_hdr ? 1 : 0;
+
+       return exp_info;
+}
+
+struct csv_info_t *create_export_csv(int argc, char **argv,
+       const char *ip_csv_marker, const char *fmt_csv_marker)
+{
+       const char *input_csv, *format_csv;
+       struct csv_info_t *csv;
+       struct export_format_t *exp_fmt = NULL;
+
+       input_csv = get_cmdline_argument(argc, argv, ip_csv_marker, 1);
+       if (!input_csv)
+               return NULL;
+
+       format_csv = get_cmdline_argument(argc, argv, fmt_csv_marker, 1);
+       if (format_csv)
+               exp_fmt = parse_export_format(format_csv);
+
+       csv = parse_csv_file(input_csv, exp_fmt);
+
+       free_exp_fmt(exp_fmt);
+
+       return csv;
+}
+
+static void copy_dev_info_from_gauge(struct csv_info_t *csv,
+       struct gauge_info_t *gauge)
+{
+       if (!csv)
+               return;
+
+       csv->header->device_num = gauge->device_num;
+       csv->header->fw_version = gauge->fw_version;
+       csv->header->fw_ver_bld = gauge->fw_ver_bld;
+}
+
+static int export_loop(struct csv_info_t *params_csv, struct csv_info_t *regs_csv,
+       struct gauge_info_t *gauge, int argc, char **argv)
+{
+       int err = 0;
+       time_t now;
+       struct tm *t = NULL;
+       char buf[MAX_USED_FLD_LEN];
+       const char *timestamp = NULL;
+       struct export_info_t *exp_info = NULL;
+
+       if (!regs_csv && !params_csv) {
+               pr_err("no valid csv for exporting\n");
+               return 0;
+       }
+
+       exp_info = get_export_info(argc, argv);
+       if (!exp_info)
+               goto end;
+
+       copy_dev_info_from_gauge(params_csv, gauge);
+       copy_dev_info_from_gauge(regs_csv, gauge);
+
+       while (1) {
+               if (exp_info->timestamp) {
+                       now = time(NULL);
+                       t = localtime(&now);
+                       strftime(buf, MAX_USED_FLD_LEN,
+                               "%m/%d/%y %H:%M:%S", t);
+                       timestamp = buf;
+               }
+
+               gauge->lock_gauge_interface(gauge);
+
+               if (!err && regs_csv) {
+                       err |= !read_regs(regs_csv, gauge);
+                       if (!err)
+                               err |= !write_csv_file(regs_csv, exp_info, timestamp);
+               }
+
+               if (!err && params_csv) {
+                       if ((err = !gauge->open_dm(gauge, argc, argv)))
+                               break;
+                       err |= !gauge->read_params(params_csv, gauge);
+                       gauge->close_dm(gauge, argc, argv);
+                       if (err)
+                               break;
+                       err |= !write_csv_file(params_csv, exp_info, timestamp);
+               }
+
+               gauge->unlock_gauge_interface(gauge);
+
+               if (err || !exp_info->period)
+                       break;
+
+               gauge->sleep_ms(exp_info->period);
+       };
+
+end:
+       gauge->unlock_gauge_interface(gauge);
+       free_exp_info(exp_info);
+
+       return !err;
+}
+
+static int export_gg_csv(int argc, char **argv)
+{
+       struct csv_info_t *csv = NULL;
+       struct gauge_info_t *gauge = NULL;
+       int ret = 0;
+
+       csv = create_export_csv(argc, argv, "--params-csv=",
+               "--format-csv=");
+       if (!csv)
+               goto end;
+
+       gauge = init_gauge(csv, argc, argv, OP_DM_READ);
+       if (!gauge)
+               goto end;
+
+       ret = export_loop(csv, NULL, gauge, argc, argv);
+
+end:
+       free_csv(csv);
+       free_gauge(gauge);
+
+       return ret;
+}
+
+static int export_regs(int argc, char **argv)
+{
+       struct csv_info_t *csv = NULL;
+       struct gauge_info_t *gauge = NULL;
+       int ret = 0;
+
+       csv = create_export_csv(argc, argv, "--regs-csv=",
+               "--regs-format-csv=");
+       if (!csv)
+               goto end;
+
+       gauge = init_gauge(csv, argc, argv, OP_REG_DUMP);
+       if (!gauge)
+               goto end;
+
+       ret = export_loop(NULL, csv, gauge, argc, argv);
+
+end:
+       free_csv(csv);
+       free_gauge(gauge);
+
+       return ret;
+}
+
+static int combined_export(int argc, char **argv)
+{
+       struct csv_info_t *regs_csv = NULL, *dm_csv = NULL;
+       struct gauge_info_t *gauge = NULL;
+       int ret = 0;
+
+       regs_csv = create_export_csv(argc, argv, "--regs-csv=",
+               "--regs-format-csv=");
+       dm_csv = create_export_csv(argc, argv, "--dm-csv=",
+               "--dm-format-csv=");
+       if (!regs_csv || !dm_csv)
+               goto end;
+
+       gauge = init_gauge(dm_csv, argc, argv, OP_DM_READ);
+       if (!gauge)
+               goto end;
+
+       ret = export_loop(dm_csv, regs_csv, gauge, argc, argv);
+
+end:
+       free_csv(regs_csv);
+       free_csv(dm_csv);
+       free_gauge(gauge);
+
+       return ret;
+}
+
+#define MAX_ROM_MONITOR_ERR_CNT 5
+
+static int rom_gauge_monitor(int argc, char **argv)
+{
+       const char *init, *save_rest;
+       struct csv_info_t *csv = NULL;
+       int ret, err, err_cnt;
+       struct export_info_t *exp_info = NULL;
+       time_t now;
+       struct tm *t = NULL;
+       char buf[MAX_USED_FLD_LEN];
+       const char *timestamp = NULL;
+       struct gauge_info_t *gauge = NULL;
+
+begin:
+       ret = err = err_cnt = 0;
+       /*
+        * Let's not rely on ITPOR and initialize the device
+        * unconditionally here. Later it will be re-initialized
+        * if we detect ITPOR in the loop
+        */
+       init = get_cmdline_argument(argc, argv, "--init-csv=", 1);
+       if (!init) {
+               pr_err("--init-csv not provided\n");
+               return 0;
+       }
+
+       csv = parse_csv_file(init, NULL);
+       if (!csv)
+               goto end;
+
+       gauge = init_gauge(csv, argc, argv, OP_DM_WRITE);
+       if (!gauge)
+               goto end;
+
+       gauge->lock_gauge_interface(gauge);
+
+       if (!gauge->open_dm(gauge, argc, argv))
+               goto end;
+
+       pr_info("Programming --init-csv %s\n", init);
+       err |= !gauge->write_params(csv, gauge);
+       if (err) {
+               pr_err("Error programming %s\n", init);
+       }
+
+       save_rest= get_cmdline_argument(argc, argv, "--save-restore-csv=", 1);
+       if (!save_rest || !file_exists(save_rest)) {
+               pr_err("--save-restore-csv= not provided or the file not"
+                       " found. periodic save will not be performed..\n");
+               goto end;
+       }
+
+       /* --save-restore-csv exists, program it */
+       free_csv(csv);
+       csv = parse_csv_file(save_rest, NULL);
+       if (!csv)
+               goto end;
+
+       /* Restore the params */
+       pr_info("restoring params from %s\n", save_rest);
+       err |= !gauge->write_params(csv, gauge);
+       gauge->close_dm(gauge, argc, argv);
+       if (err) {
+               pr_err("Error initializing ROM gauge. Aborting monitoring!");
+               goto end;
+       }
+
+
+       pr_info("Initialization successful! Start monitoring..\n");
+       /*
+        * Close the --save-restore-csv file, we need to reopen it with
+        * "w" mode for saving params
+        */
+       free_file(csv->header->file);
+       csv->header->file = NULL;
+
+       exp_info = get_export_info(argc, argv);
+       exp_info->no_hdr = 0;
+       if (!exp_info->period) {
+               pr_err("--period= not provided, assuming 1 min\n");
+               exp_info->period = 60000;
+       }
+
+       err_cnt = 0;
+       while (err_cnt < MAX_ROM_MONITOR_ERR_CNT) {
+
+               gauge->lock_gauge_interface(gauge);
+
+               if (gauge->itpor(gauge)) {
+                       free_gauge(gauge);
+                       gauge = NULL;
+                       free_csv(csv);
+                       csv = NULL;
+                       pr_err("Reset detected.. Re-initializing..\n")
+                       goto begin;
+               }
+
+               err = !gauge->read_params(csv, gauge);
+               if (exp_info->timestamp) {
+                       now = time(NULL);
+                       t = localtime(&now);
+                       strftime(buf, MAX_USED_FLD_LEN, "%m/%d/%y %H:%M:%S", t);
+                       timestamp = buf;
+               }
+
+               gauge->unlock_gauge_interface(gauge);
+
+               if (err) {
+                       err_cnt++;
+                       continue;
+               } else {
+                       err_cnt = 0;
+                       exp_info->fd = fopen(save_rest, "w");
+                       if (exp_info->fd) {
+                               pr_info("saving params to %s\n", save_rest);
+                               err |= !write_csv_file(csv, exp_info, timestamp);
+                               fclose(exp_info->fd);
+                       }
+               }
+
+
+               gauge->sleep_ms(exp_info->period);
+       }
+
+       if (err_cnt >= MAX_ROM_MONITOR_ERR_CNT)
+               pr_err("Error count exceeded limit.. Exiting..\n");
+
+end:
+       free_csv(csv);
+
+       if (gauge) {
+               gauge->unlock_gauge_interface(gauge);
+               free_gauge(gauge);
+       }
+
+       free_exp_info(exp_info);
+
+       return ret && !err;
+}
+
+static void free_bqfs_cmd(struct bqfs_cmd_t *cmd)
+{
+       if (!cmd)
+               return;
+
+       if (cmd->buf)
+               free(cmd->buf);
+
+       if (cmd->tmp_buf)
+               free(cmd->tmp_buf);
+
+       free(cmd);
+}
+
+
+/*
+ * Returns:
+ * number of bytes extracted
+ */
+static int extract_hex_byte_stream(char **str, uint8_t *buf, int max_len)
+{
+       char *tok, *tmp;
+       unsigned long val;
+       int i = 0;
+
+       /*
+        * We can do this using strtoul without tokenizing.
+        * But let's tokenize in the interest of better
+        * error handling
+        */
+       while ((tok = get_delim_separated_item(str, ' ')) && i < max_len) {
+               val = strtoul(tok, &tmp, 16);
+               //pr_dbg("%s\n", tok);
+               if (tmp == tok) {
+                       pr_err("token appears to be not a hex number: %s\n",
+                               tok);
+                       return 0;
+               }
+
+               buf[i++] = (uint8_t) val;
+       }
+
+       return i;
+}
+
+#ifdef BQTOOL_DEBUG
+static void pr_dbg_bytes(uint8_t *buf, uint32_t size)
+{
+       uint32_t i;
+
+       for (i = 0; i < size; i++)
+               pr_dbg_raw(" %02x", buf[i]);
+
+}
+#else
+static void pr_dbg_bytes(uint8_t *buf, uint32_t size)
+{
+}
+#endif
+
+
+/*
+ * Returns:
+ * 1  - Command execution successful
+ * -1 - Command execution failed
+ */
+static int bqfs_exec_cmd(struct gauge_info_t *gauge, struct bqfs_cmd_t *cmd)
+{
+       int cmd_base_len, ret = 1;
+       uint8_t data_len, *cmd_buf;
+
+       cmd_base_len = (cmd->mode == BQFS_HDQ) ? 1 : 2;
+
+       /*
+        * I2C:
+        * cmd->buf[0] => slave address
+        * cmd->buf[1] => reg address
+        * cmd->buf[2] onwards data
+        *
+        * HDQ
+        * cmd->buf[0] => reg address
+        * cmd->buf[1] onwards data
+        *
+        * For HDQ, slave address assigned below is not correct,
+        * but it's never used.
+        */
+       gauge->slave_addr = cmd->buf[0];
+
+       /* Register address is part of the command data in the read/write API */
+       cmd_buf = &cmd->buf[cmd_base_len - 1];
+
+       /* data_len is the actual data length, doesn't count reg address */
+       data_len = cmd->len - cmd_base_len;
+
+       switch (cmd->cmd) {
+       case 'R':
+               /*
+                * This command is not really useful now
+                * It may be useful later when implementing dfpo
+                * support
+                */
+               ret = do_read(gauge, cmd_buf, data_len);
+               break;
+       case 'W':
+               ret = do_write(gauge, cmd_buf, data_len);
+               break;
+       case 'C':
+               /*
+                * Copy data to compare later. Command buffer
+                * includes register + data
+                */
+               memcpy(cmd->tmp_buf, &cmd_buf[1], data_len);
+               ret = do_read(gauge, cmd_buf, data_len);
+               if (!ret || memcmp(cmd->tmp_buf, &cmd_buf[1], data_len)) {
+                       pr_err("\nCommand C failed at line %d:\n",
+                               cmd->line_num);
+                       pr_err("Expected data: ");
+                       pr_dbg_bytes(cmd->tmp_buf, data_len);
+                       pr_err("\nReceived data: ");
+                       pr_dbg_bytes(&cmd_buf[1], data_len);
+                       pr_err("\n");
+                       ret = -1;
+               }
+               break;
+       case 'X':
+               gauge->sleep_ms(cmd->delay);
+               break;
+       default:
+               ret = -1;
+               break;
+       }
+
+       if (ret <= 0) {
+               pr_err("command execution failed at line %d\n", cmd->line_num);
+               return -1;
+       }
+
+       return 1;
+}
+
+/*
+ * Returns:
+ * 1  - successfully found a new command
+ * 0  - End of stream
+ * -1 - Error parsing command
+ */
+static int bqfs_get_cmd(struct file_info_t *file, struct bqfs_cmd_t *cmd)
+{
+       char *tok, *buf, *tmp;
+       int ret, cmd_base_len;
+       unsigned long int val;
+
+       pr_dbg(">>\n");
+       /*
+        * Get the first non-comment line:
+        * Comment liens start with ';'
+        */
+       while ((buf = read_line(file)) && buf[0] == ';');
+
+       if (!buf)
+               return 0;
+
+       tok = get_delim_separated_item(&buf, ':');
+       if (!tok || (strlen(tok) != 1)) {
+               pr_err("Error parsing command at line %d tok=%s buf=%s\n",
+                       file->line_num, tok, buf);
+               return -1;
+       }
+
+       cmd->cmd = toupper(tok[0]);
+       cmd->line_num = file->line_num;
+
+       cmd_base_len = (cmd->mode == BQFS_HDQ) ? 1 : 2;
+
+       pr_dbg_raw("%c:", cmd->cmd);
+
+       switch (cmd->cmd) {
+       case 'R':
+               ret = extract_hex_byte_stream(&buf, cmd->buf, cmd_base_len);
+               if (ret != cmd_base_len)  {
+                       pr_err("error parsing read cmd at line %d buf=%s\n",
+                               file->line_num, buf);
+                       return -1;
+               }
+               val = strtoul(buf, &tmp, 10);
+               cmd->len = (uint8_t) val;
+               if (buf == tmp || val != cmd->len) {
+                       pr_err("error parsing rd cmd len at line %d buf=%s\n",
+                               file->line_num, buf);
+                       return -1;
+               }
+               pr_dbg_bytes(cmd->buf, cmd_base_len);
+               pr_dbg_raw("%d\n", cmd->len);
+               break;
+       case 'W':
+       case 'C':
+               //pr_dbg("%x\n", &buf);
+               cmd->len = extract_hex_byte_stream(&buf, cmd->buf, cmd->max_len);
+               if (cmd->len < cmd_base_len + 1)  {
+                       pr_err("error parsing cmd bytes at line %d buf=%s\n",
+                               file->line_num, buf);
+                       return -1;
+               }
+
+               pr_dbg_bytes(cmd->buf, cmd->len);
+               pr_dbg_raw("\n");
+               break;
+       case 'X':
+               val = strtoul(buf, &tmp, 10);
+               cmd->delay = (uint16_t) val;
+               if (buf == tmp || val != cmd->delay) {
+                       pr_err("Error parsing delay at line %d buf=%s\n",
+                               file->line_num, buf);
+                       return -1;
+               }
+               pr_dbg_bytes(cmd->buf, cmd_base_len);
+               pr_dbg_raw(" %d\n", cmd->delay);
+               break;
+       default:
+               pr_err("No command or unexpected command at"
+                       " line %d tok=\"%s\" buf=\"%s\"",
+                       file->line_num, tok, buf);
+               return -1;
+       }
+
+       return 1;
+}
+
+static int bqfs_flash(int argc, char **argv)
+{
+       struct file_info_t *file = NULL;
+       const char *fname, *max_block_len, *mode;
+       struct bqfs_cmd_t *cmd = NULL;
+       struct gauge_info_t *gauge = NULL;
+       long long int max_len = 0;
+       int ret = 0, i;
+       uint8_t slave_addr;
+
+       fname = get_cmdline_argument(argc, argv, "--bqfs-file=", 1);
+       if (!fname) {
+               pr_err("--bqfs-file not provided\n");
+               goto end;
+       }
+
+       cmd = (struct bqfs_cmd_t *) malloc(sizeof(struct bqfs_cmd_t));
+       if (!cmd)
+               goto end;
+       mode = get_cmdline_argument(argc, argv, "--mode=", 1);
+
+       if (mode && strcicmp(mode, "hdq") == 0)
+               cmd->mode = BQFS_HDQ;
+       else
+               cmd->mode = BQFS_I2C;
+
+       max_block_len = get_cmdline_argument(argc, argv,
+               "--max-block-len=", 1);
+       if (max_block_len)
+               extract_integer(max_block_len, &max_len);
+       if (!max_len)
+               max_len = BQFS_MAX_DATA_BLOCK_LEN;
+       cmd->max_len = (cmd->mode == BQFS_HDQ) ? max_len + 1: max_len + 2;
+
+       file = create_file(fname, "r", (cmd->max_len + 2) * 3);
+       if (!file)
+               goto end;
+
+       cmd->buf = (uint8_t*) malloc(cmd->max_len);
+       cmd->tmp_buf = (uint8_t*) malloc(cmd->max_len);
+       if (!cmd->buf || !cmd->tmp_buf)
+               goto end;
+
+        gauge = init_gauge(NULL, argc, argv, OP_BQFS_FLASH);
+       if (!gauge)
+               goto end;
+
+       gauge->open_dm(gauge, argc, argv);
+       /* Save the FW mode slave addr before executing the bqfs */
+       slave_addr = gauge->slave_addr;
+
+       gauge->lock_gauge_interface(gauge);
+       ret = bqfs_get_cmd(file, cmd);
+       while(ret > 0) {
+               i = 0;
+               while (1) {
+                       ret = bqfs_exec_cmd(gauge, cmd);
+                       if ((i++ >= 3) || ret > 0)
+                               break;
+                       pr_err("Retrying bqfs cmd line %d..\n", cmd->line_num);
+               }
+
+               if (ret == 1)
+                       fputc('.', stdout);
+               else
+                       break;
+
+               ret = bqfs_get_cmd(file, cmd);
+       }
+       fputc('\n', stdout);
+
+       if (get_cmdline_argument(argc, argv, "--reset", 0))
+               gauge->reset(gauge);
+
+       /* Restore the slave adress */
+       gauge->slave_addr = slave_addr;
+       gauge->close_dm(gauge, argc, argv);
+
+       gauge->unlock_gauge_interface(gauge);
+       /*
+        * If ret == 0, reached end of stream without any errors
+        */
+       if (ret == 0)
+               ret = 1;
+       else
+               ret = 0;
+
+end:
+       pr_dbg("<<\n");
+       free_file(file);
+       free_bqfs_cmd(cmd);
+       free_gauge(gauge);
+
+       return ret;
+}
+
+static const struct command_t commands[] = {
+#ifdef CMD_IMPORT_GG_CSV
+       {"--import-gg-csv", import_gg_csv, "--params-csv=<gg-csv> [--unseal-key=<key> --fullaccess-key=<key> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num> --reset --exit-seal=<seal-state>]"},
+#endif
+#ifdef CMD_ROM_GAUGE_MONITOR
+       {"--rom-gauge-monitor", rom_gauge_monitor, "--init-csv=<gg-csv> --save-restore-csv=<gg-csv> --period=<prd-ms> [--timestamp --unseal-key=<key> --fullaccess-key=<key> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num> --exit-seal=<seal-state>]"},
+#endif
+#ifdef CMD_BQFS_FLASH
+       {"--bqfs-flash", bqfs_flash, "--bqfs-flash=<bqfs/dffs file> [--mode=<interface> --max-block-len=<max-blk-len> --unseal-key=<key> --fullaccess-key=<key> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num> --reset --exit-seal=<seal-state>]"},
+#endif
+#ifdef CMD_EXPORT_GG_CSV
+       {"--export-gg-csv", export_gg_csv, "--params-csv=<gg-csv> [--output-csv=<csv-file> --format-csv=<csv-file> --period=<prd-ms> --timestamp --no-header --unseal-key=<key> --fullaccess-key=<key> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num> --exit-seal=<seal-state>]"},
+#endif
+#ifdef CMD_EXPORT_REGS
+       {"--export-regs", export_regs, "--regs-csv=<regs-csv> [--output-csv=<csv-file> --regs-format-csv=<regs-csv> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num>]"},
+#endif
+#ifdef CMD_COMBINED_EXPORT
+       {"--combined-export", combined_export, "--dm-csv=<gg-csv> --regs-csv=<regs-csv> [--output-csv=<csv-file> --dm-format-csv==<csv-rile> --regs-format-csv=<regs-csv> --period=<prd-ms> --timestamp --no-header --unseal-key=<key> --fullaccess-key=<key> --i2c-dev-file=<dev-file> --i2c-bus=<bus-num> --exit-seal=<seal-state>]"},
+#endif
+};
+
+const char *bqt_readme = "https://git.ti.com/bms-linux/bqtool/blobs/master/README";
+
+static void usage(int cmd_ind)
+{
+       unsigned int i;
+
+       pr_err_raw("Usage:\n");
+       if (cmd_ind == -1) {
+               for (i = 0; i < ARRAY_SIZE(commands); i++) {
+                       pr_err_raw("%s %s\n\n", commands[i].cmd_name,
+                               commands[i].help);
+               }
+       } else {
+               pr_err_raw("%s %s\n\n", commands[cmd_ind].cmd_name,
+                       commands[cmd_ind].help);
+       }
+
+       pr_err_raw("Please see %s for details of command options\n",
+               bqt_readme);
+}
+
+int main(int argc, char **argv)
+{
+       unsigned int i;
+       int ret = -1;
+
+       if (argc < 2) {
+               usage(-1);
+               return -1;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(commands); i++) {
+               if (strcmp(argv[1], commands[i].cmd_name) == 0) {
+                       ret = commands[i].cmd_func(argc, argv);
+                       break;
+               }
+       }
+
+       if (ret == -1) {
+               pr_err("Unrecognized command: %s\n", argv[1]);
+               usage(-1);
+               return -1;
+       } else if (ret == 0) {
+               pr_err("comamnd \"%s\" failed\n: ", argv[1]);
+               usage(i);
+               return -1;
+       } else {
+               pr_info("comamnd \"%s\" executed successfully!\n", argv[1]);
+       }
+
+       return 0;
+}