]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - bms-linux/bqtool.git/blob - bqtool.c
add i2c-dev.h to git
[bms-linux/bqtool.git] / bqtool.c
1 /*
2  * Copyright (C) 2014 Texas Instruments Inc
3  *
4  * Aneesh V <aneesh@ti.com>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
19 /*
20  * Android utility for programming and debugging TI's bq27xxx
21  * family of gas gauges
22  *
23  * Commands supported:
24  * --flash :
25  *      bqtool --flash <bqfs file|dffs file>
26  *      Eg: bqtool --flash /system/bin/bq27520_v302.bqfs
27  *
28  *      For details of flashstream file format(.bqfs/.dffs) see:
29  *      http://www.ti.com.cn/cn/lit/an/slua541a/slua541a.pdf
30  *
31  */
33 #include <stdio.h>
34 #include <string.h>
35 #include <i2c-dev.h>
36 #include <sys/ioctl.h>
37 #include <unistd.h>
38 #include <stdint.h>
39 #include <fcntl.h>
40 #include <stdbool.h>
42 /* #define BQPROG_DEBUG */
44 #define CMD_MAX_DATA_SIZE       96
45 #define MAX_LINE_LEN            ((CMD_MAX_DATA_SIZE + 4) * 3)
46 #define RETRY_LIMIT             3
47 #define CMD_RETRY_DELAY         100 /* in ms */
48 #define DEFAULT_POLL_INTVL      360
49 #define I2C_BUS                 "2"
50 #define I2C_DEV                 "/dev/i2c-"I2C_BUS
51 #define SYSFS_POLL_INTVL "/sys/module/bq27x00_battery/parameters/poll_interval"
53 #ifdef __GNUC__
54 #define __PACKED        __attribute__((packed))
55 #else
56 #error "Make sure structure cmd_t is packed"
57 #endif
59 typedef enum {
60         CMD_INVALID = 0,
61         CMD_R,  /* Read */
62         CMD_W,  /* Write */
63         CMD_C,  /* Compare */
64         CMD_X,  /* Delay */
65 } cmd_type_t;
67 /*
68  * DO NOT change the order of fields - particularly reg
69  * should be immediately followed by data
70  */
71 typedef struct {
72         cmd_type_t cmd_type;
73         uint8_t addr;
74         uint8_t reg;
75         union {
76                 uint8_t bytes[CMD_MAX_DATA_SIZE + 1];
77                 uint16_t delay;
78         } data;
79         uint8_t data_len;
80         uint32_t line_num;
81 } __PACKED cmd_t;
83 static uint32_t line_num;
85 /*
86  * Print usage of all commands when cmd == NULL
87  * Otherwise print the usage of the respective command
88  * Currently we have only one commnd
89  */
90 static void usage(const char *cmd)
91 {
92         fprintf(stderr, "Usage: bqtool --flash <bqfs-file|dffs-file>\n");
93 }
95 static void print_delay(cmd_t *cmd)
96 {
97         fprintf(stdout, "delay: %d", cmd->data.delay);
98 }
100 static void print_buf(FILE *fd, uint8_t *buf, uint8_t len)
102         uint8_t i;
104         for (i = 0; i < len; i++)
105                 fprintf(fd, " %02x", buf[i]);
108 static void print_data(cmd_t *cmd)
110         int i;
112         fprintf(stdout, "data: ");
113         print_buf(stdout, cmd->data.bytes, cmd->data_len);
115 static void print_addr_reg(cmd_t *cmd)
117         fprintf(stdout, "addr: %02x ", cmd->addr);
118         fprintf(stdout, "reg: %02x ", cmd->reg);
121 #ifdef BQPROG_DEBUG
122 static void print_cmd(cmd_t *cmd)
124         switch (cmd->cmd_type) {
125         case CMD_R:
126                 fprintf(stdout, "R: ");
127                 print_addr_reg(cmd);
128                 break;
129         case CMD_W:
130                 fprintf(stdout, "W: ");
131                 print_addr_reg(cmd);
132                 print_data(cmd);
133                 break;
134         case CMD_C:
135                 fprintf(stdout, "C: ");
136                 print_addr_reg(cmd);
137                 print_data(cmd);
138                 break;
139         case CMD_X:
140                 fprintf(stdout, "X: ");
141                 print_delay(cmd);
142                 break;
143         default:
144                 fprintf(stdout, "Unknown: ");
145                 break;
146         }
147         fprintf(stdout, "\n");
149 #else
150 static void print_cmd(cmd_t *cmd)
153 #endif
155 static int read_bq_poll_intvl(void)
157         int poll_file = -1;
158         int poll_intvl = -1;
159         char buf[20];
161         poll_file = open(SYSFS_POLL_INTVL, O_RDONLY);
163         if ((poll_file >= 0) && read(poll_file, buf, 20))
164                 sscanf(buf, "%d", &poll_intvl);
165         else
166                 fprintf(stderr, "Failed to read %s\n", SYSFS_POLL_INTVL);
168         if (poll_file >= 0)
169                 close(poll_file);
171         return poll_intvl;
174 static bool write_bq_poll_intvl(int poll_intvl)
176         int poll_file = -1;
177         char buf[20];
179         poll_file = open(SYSFS_POLL_INTVL, O_RDWR);
181         if (poll_file >= 0) {
182                 sprintf(buf, "%d", poll_intvl);
183                 write(poll_file, buf, 20);
184                 close(poll_file);
185         }
187         if (poll_intvl == read_bq_poll_intvl())
188                 return true;
189         else
190                 return false;
193 static bool i2c_rw(int i2c_file, cmd_t *cmd, int write)
195         int ret;
196         struct i2c_rdwr_ioctl_data i2c_data;
197         char *op;
198         /* msg[0] for write command and msg[1] for read command */
199         struct i2c_msg msgs[2];
201         /* Linux expects 7 bit address */
202         msgs[0].addr = cmd->addr >> 1;
203         /* reg is data too as far as I2C xfr is concerned */
204         msgs[0].buf = (char *)&cmd->reg;
205         msgs[0].flags = 0;
207         if (write) {
208                 msgs[0].len = cmd->data_len + 1;
209                 i2c_data.nmsgs = 1;
210                 op = "write";
211         } else {
212                 msgs[0].len = 1;
214                 /* read command */
215                 msgs[1].addr = cmd->addr >> 1;
216                 msgs[1].buf = (char *)cmd->data.bytes;
217                 msgs[1].flags = I2C_M_RD;
218                 msgs[1].len = cmd->data_len;
220                 i2c_data.nmsgs = 2;
221                 op = "read";
222         }
224         i2c_data.msgs = msgs;
225         ret = ioctl(i2c_file, I2C_RDWR, &i2c_data);
226         if (ret < 0) {
227                 fprintf(stderr, "I2C %s failed at line %d error = %d\n",
228                         op, cmd->line_num, ret);
229                 return false;
230         }
232         return true;
235 static bool do_exec_cmd(int i2c_file, cmd_t *cmd)
237         uint8_t tmp_buf[CMD_MAX_DATA_SIZE];
239         switch (cmd->cmd_type) {
240         case CMD_R:
241                 return i2c_rw(i2c_file, cmd, 0);
243         case CMD_W:
244                 return i2c_rw(i2c_file, cmd, 1);
246         case CMD_C:
247                 memcpy(tmp_buf, cmd->data.bytes, cmd->data_len);
248                 if (!i2c_rw(i2c_file, cmd, 0))
249                         return false;
250                 if (memcmp(tmp_buf, cmd->data.bytes, cmd->data_len)) {
251                         fprintf(stderr, "\nCommand C failed at line %d:\n",
252                                 cmd->line_num);
253                         fprintf(stderr, "Expected data:");
254                         print_buf(stderr, tmp_buf, cmd->data_len);
255                         fprintf(stderr, "\nReceived data:");
256                         print_buf(stderr, cmd->data.bytes, cmd->data_len);
257                         fprintf(stderr, "\n");
258                         return false;
259                 }
260                 return true;
262         case CMD_X:
263                 usleep(cmd->data.delay * 1000);
264                 return true;
266         default:
267                 fprintf(stderr, "Unsupported command at line %d\n",
268                         cmd->line_num);
269                 return false;
270         }
273 static bool execute_cmd(int i2c_file, cmd_t *cmd)
275         int i = 1;
276         bool ret;
278         ret = do_exec_cmd(i2c_file, cmd);
280         while (!ret && i < RETRY_LIMIT) {
281                 usleep(CMD_RETRY_DELAY * 1000);
282                 ret = do_exec_cmd(i2c_file, cmd);
283                 i++;
284         }
286         if (!ret) {
287                 fprintf(stderr, "Command execution failed at line %d"
288                         " addr - 0x%02x reg - 0x%02x, tried %d times\n",
289                         cmd->line_num, cmd->addr, cmd->reg, RETRY_LIMIT);
290         }
292         return ret;
295 static bool get_delay(uint16_t *delay)
297         char *tok;
298         uint32_t temp;
300         tok = strtok(NULL, " ");
301         if (!tok)
302                 return false; /*end of line or file */
304         if (1 != sscanf(tok, "%u", &temp)) {
305                 fprintf(stderr, "Syntax error while parsing delay at line %d\n",
306                         line_num);
307                 return false; /* syntax error */
308         }
310         if (temp > UINT16_MAX) {
311                 fprintf(stderr, "Command X delay too high at line %d - %dms\n",
312                         line_num, temp);
313                 return false;
314         }
316         *delay = (uint16_t)temp;
317         return true;
321 /*
322  * Returns:
323  *       0: success
324  *       1: EOF
325  *      -1: Parse error
326  */
327 static int get_byte(uint8_t *byte)
329         char *tok;
330         unsigned int temp;
332         tok = strtok(NULL, " \t\r\n");
333         if (!tok)
334                 return 1; /*end of line or file */
336         if ((strlen(tok) != 2) || (sscanf(tok, "%2x", &temp) != 1)) {
337                         fprintf(stderr, "Syntax error at line %d, token - %s,"
338                                 " temp - %x\n", line_num, tok, temp);
339                         return -1; /* syntax error */
340         }
342         *byte = (uint8_t)temp;
344         return 0;       /* success */
347 static bool get_addr_n_reg(cmd_t *cmd)
349         if (get_byte(&cmd->addr))
350                 return false;
352         if (get_byte(&cmd->reg))
353                 return false;
355         return true;
358 static bool get_data_bytes(cmd_t *cmd)
360         int ret, i = 0;
361         cmd->data_len = 0;
363         do {
364                 ret = get_byte(&cmd->data.bytes[i++]);
365                 if (ret == -1)
366                         return false;
367         } while ((ret == 0) && (i <= CMD_MAX_DATA_SIZE));
369         if (ret == 0) {
370                 fprintf(stderr, "More than allowed number of data bytes at"
371                         " line %d, data_len %d, i %d\n", cmd->line_num,
372                         cmd->data_len, i);
373                 return false;
374         }
376         cmd->data_len = i - 1;
378         return true;
381 static bool get_line(FILE *bqfs_file, char **buffer)
383         int c;
384         int i = 0;
385         bool ret = true;
386         char *buf;
388         buf = malloc(MAX_LINE_LEN);
389         line_num++;
391         while (1) {
392                 c = fgetc(bqfs_file);
394                 if (feof(bqfs_file)) {
395 #ifdef BQPROG_DEBUG
396                         fprintf(stdout, "EOF\n");
397 #endif
398                         break;
399                 } else if (ferror(bqfs_file)) {
400                         fprintf(stderr, "File read error\n");
401                         ret = false;
402                         break;
403                 }
405                 if (((c == '\r') || (c == '\n') || (c == '\t')
406                         || (c == ' ')) && (i == 0)) {
407                         /*
408                          * Skip leading white space, if any, at the beginning
409                          * of the line because this interferes with strtok
410                          */
411                         fprintf(stderr, "Leading whitespace at line %d\n",
412                                 line_num);
413                         if (c == '\n')
414                                 line_num++;
415                         continue;       /* blank line, let's continue */
416                 } else if (c == '\n') {
417                         /* We've reached end of line */
418                         break;
419                 }
421                 buf[i++] = c;
423                 if (i == MAX_LINE_LEN) {
424                         /*
425                          * Re-allocate in case the line is longer than
426                          * expected
427                          */
428                         buf = realloc(buf, MAX_LINE_LEN * 2);
429                         fprintf(stderr, "Line %d longer than expected,"
430                                 " reallocating..\n", line_num);
431                 } else if (i == MAX_LINE_LEN * 2) {
432                         /*
433                          * The line is already twice the expected maximum length
434                          * - maybe the bqfs/dffs needs to be fixed
435                          */
436                         fprintf(stderr, "Line %d too long, abort parsing..\n",
437                                 line_num);
438                         ret = false;
439                         break;
440                 }
441         }
443         *buffer = buf;
444         buf[i] = '\0';
446         if (i < 1)
447                 ret = false;
449         return ret;
452 static bool get_cmd(FILE *bqfs_file, cmd_t *cmd)
454         char *res;
455         char *tok;
456         char *buf = NULL;
457         int ret;
459         if (!get_line(bqfs_file, &buf))
460                 goto error;
462         cmd->line_num = line_num;
463         tok = strtok(buf, ":");
464         if (!tok || (strlen(tok) != 1)) {
465                 fprintf(stderr, "Error parsing command at line %d tok=%s"
466                         " buf=%s", line_num, tok, buf);
467                 goto error;
468         }
470         switch (tok[0]) {
471         case 'R':
472         case 'r':
473                 cmd->cmd_type = CMD_R;
474                 if (!get_addr_n_reg(cmd))
475                         goto error;
476                 break;
477         case 'W':
478         case 'w':
479                 cmd->cmd_type = CMD_W;
480                 if (!get_addr_n_reg(cmd))
481                         goto error;
482                 if (!get_data_bytes(cmd))
483                         goto error;
484                 break;
485         case 'C':
486         case 'c':
487                 cmd->cmd_type = CMD_C;
488                 if (!get_addr_n_reg(cmd))
489                         goto error;
490                 if (!get_data_bytes(cmd))
491                         goto error;
492                 break;
493         case 'X':
494         case 'x':
495                 cmd->cmd_type = CMD_X;
496                 if (!get_delay(&cmd->data.delay))
497                         goto error;
498                 break;
499         default:
500                 fprintf(stderr, "No command or unexpected command at"
501                         " line %d tok=\"%s\" buf=\"%s\"",
502                         line_num, tok, buf);
503                 goto error;
504         }
506         print_cmd(cmd);
507         free(buf);
508         return true;
510 error:
511         cmd->cmd_type = CMD_INVALID;
512         free(buf);
513         return false;
516 int bqfs_flash(char *fname)
518         int poll_intvl = -1;
519         int i2c_file = -1;
520         int poll_file = -1;
521         FILE *bqfs_file = NULL;
522         char *line = NULL;
523         cmd_t *cmd = NULL;
524         int ret = 0;
525         char buf[20];
527         bqfs_file = fopen(fname, "r");
528         if (!bqfs_file) {
529                 usage("--flash");
530                 ret = -1;
531                 goto end;
532         }
534         poll_intvl = read_bq_poll_intvl();
535         /* Turn off polling */
536         if (!write_bq_poll_intvl(0)) {
537                 fprintf(stderr, "Failed to stop driver polling\n");
538                 ret = -1;
539                 goto end;
540         }
542         i2c_file = open(I2C_DEV, O_RDWR);
543         if (i2c_file < 0) {
544                 fprintf(stderr, "Failed to open I2C device %s\n", I2C_DEV);
545                 ret = -1;
546                 goto end;
547         }
549         cmd = malloc(sizeof(cmd_t));
551         while (get_cmd(bqfs_file, cmd) && execute_cmd(i2c_file, cmd))
552                 fputc('.', stdout);
554         if (feof(bqfs_file)) {
555                 fprintf(stdout, "\n%s programmed successfully!\n", fname);
556                 ret = 0;
557         } else {
558                 fprintf(stdout, "\nprogramming %s failed!!\n", fname);
559                 ret = -1;
560         }
562 end:
563         if (poll_intvl >= 0) {
564                 if (!write_bq_poll_intvl(poll_intvl))
565                         fprintf(stderr, "Failed to restore driver polling\n");
566         }
568         if (poll_file >= 0)
569                 close(poll_file);
571         if (i2c_file >= 0)
572                 close(i2c_file);
574         if (bqfs_file)
575                 fclose(bqfs_file);
577         if (cmd)
578                 free(cmd);
580         return ret;
583 int main(int argc, char **argv)
585         if ((argc >= 3) && (strncmp(argv[1], "--flash", 7) == 0)) {
586                 return bqfs_flash(argv[2]);
587         } else {
588                 usage(argv[0]);
589                 return -1;
590         }