summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (from parent 1: 362a60b)
raw | patch | inline | side by side (from parent 1: 362a60b)
author | Aneesh V <aneesh@ti.com> | |
Wed, 12 Feb 2014 18:05:25 +0000 (10:05 -0800) | ||
committer | Aneesh V <aneesh@ti.com> | |
Wed, 12 Feb 2014 18:05:25 +0000 (10:05 -0800) |
Signed-off-by: Aneesh V <aneesh@ti.com>
bqtool.c | [new file with mode: 0644] | patch | blob |
diff --git a/bqtool.c b/bqtool.c
--- /dev/null
+++ b/bqtool.c
@@ -0,0 +1,577 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <i2c-dev.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <stdbool.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;
+} __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 = (char *)&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 = (char *)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;
+ int ret;
+
+ if (!get_line(bqfs_file, &buf))
+ 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;
+ }
+}