84384cdf0d5c20afd38a780c20ba79f4953602e4
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 #include <stdio.h>
20 #include <string.h>
21 #include <i2c-dev.h>
22 #include <sys/ioctl.h>
23 #include <unistd.h>
24 #include <stdint.h>
25 #include <fcntl.h>
26 #include <stdbool.h>
28 /* #define BQPROG_DEBUG */
30 #define CMD_MAX_DATA_SIZE 96
31 #define MAX_LINE_LEN ((CMD_MAX_DATA_SIZE + 4) * 3)
32 #define RETRY_LIMIT 3
33 #define CMD_RETRY_DELAY 100 /* in ms */
34 #define DEFAULT_POLL_INTVL 360
35 #define I2C_BUS "2"
36 #define I2C_DEV "/dev/i2c-"I2C_BUS
37 #define SYSFS_POLL_INTVL "/sys/module/bq27x00_battery/parameters/poll_interval"
39 #ifdef __GNUC__
40 #define __PACKED __attribute__((packed))
41 #else
42 #error "Make sure structure cmd_t is packed"
43 #endif
45 typedef enum {
46 CMD_INVALID = 0,
47 CMD_R, /* Read */
48 CMD_W, /* Write */
49 CMD_C, /* Compare */
50 CMD_X, /* Delay */
51 } cmd_type_t;
53 /*
54 * DO NOT change the order of fields - particularly reg
55 * should be immediately followed by data
56 */
57 typedef struct {
58 cmd_type_t cmd_type;
59 uint8_t addr;
60 uint8_t reg;
61 union {
62 uint8_t bytes[CMD_MAX_DATA_SIZE + 1];
63 uint16_t delay;
64 } data;
65 uint8_t data_len;
66 uint32_t line_num;
67 } __PACKED cmd_t;
69 static uint32_t line_num;
71 /*
72 * Print usage of all commands when cmd == NULL
73 * Otherwise print the usage of the respective command
74 * Currently we have only one commnd
75 */
76 static void usage(const char *cmd)
77 {
78 fprintf(stderr, "Usage: bqtool --flash <bqfs-file|dffs-file>\n");
79 }
81 static void print_delay(cmd_t *cmd)
82 {
83 fprintf(stdout, "delay: %d", cmd->data.delay);
84 }
86 static void print_buf(FILE *fd, uint8_t *buf, uint8_t len)
87 {
88 uint8_t i;
90 for (i = 0; i < len; i++)
91 fprintf(fd, " %02x", buf[i]);
92 }
94 static void print_data(cmd_t *cmd)
95 {
96 int i;
98 fprintf(stdout, "data: ");
99 print_buf(stdout, cmd->data.bytes, cmd->data_len);
100 }
101 static void print_addr_reg(cmd_t *cmd)
102 {
103 fprintf(stdout, "addr: %02x ", cmd->addr);
104 fprintf(stdout, "reg: %02x ", cmd->reg);
105 }
107 #ifdef BQPROG_DEBUG
108 static void print_cmd(cmd_t *cmd)
109 {
110 switch (cmd->cmd_type) {
111 case CMD_R:
112 fprintf(stdout, "R: ");
113 print_addr_reg(cmd);
114 break;
115 case CMD_W:
116 fprintf(stdout, "W: ");
117 print_addr_reg(cmd);
118 print_data(cmd);
119 break;
120 case CMD_C:
121 fprintf(stdout, "C: ");
122 print_addr_reg(cmd);
123 print_data(cmd);
124 break;
125 case CMD_X:
126 fprintf(stdout, "X: ");
127 print_delay(cmd);
128 break;
129 default:
130 fprintf(stdout, "Unknown: ");
131 break;
132 }
133 fprintf(stdout, "\n");
134 }
135 #else
136 static void print_cmd(cmd_t *cmd)
137 {
138 }
139 #endif
141 static int read_bq_poll_intvl(void)
142 {
143 int poll_file = -1;
144 int poll_intvl = -1;
145 char buf[20];
147 poll_file = open(SYSFS_POLL_INTVL, O_RDONLY);
149 if ((poll_file >= 0) && read(poll_file, buf, 20))
150 sscanf(buf, "%d", &poll_intvl);
151 else
152 fprintf(stderr, "Failed to read %s\n", SYSFS_POLL_INTVL);
154 if (poll_file >= 0)
155 close(poll_file);
157 return poll_intvl;
158 }
160 static bool write_bq_poll_intvl(int poll_intvl)
161 {
162 int poll_file = -1;
163 char buf[20];
165 poll_file = open(SYSFS_POLL_INTVL, O_RDWR);
167 if (poll_file >= 0) {
168 sprintf(buf, "%d", poll_intvl);
169 write(poll_file, buf, 20);
170 close(poll_file);
171 }
173 if (poll_intvl == read_bq_poll_intvl())
174 return true;
175 else
176 return false;
177 }
179 static bool i2c_rw(int i2c_file, cmd_t *cmd, int write)
180 {
181 int ret;
182 struct i2c_rdwr_ioctl_data i2c_data;
183 char *op;
184 /* msg[0] for write command and msg[1] for read command */
185 struct i2c_msg msgs[2];
187 /* Linux expects 7 bit address */
188 msgs[0].addr = cmd->addr >> 1;
189 /* reg is data too as far as I2C xfr is concerned */
190 msgs[0].buf = (char *)&cmd->reg;
191 msgs[0].flags = 0;
193 if (write) {
194 msgs[0].len = cmd->data_len + 1;
195 i2c_data.nmsgs = 1;
196 op = "write";
197 } else {
198 msgs[0].len = 1;
200 /* read command */
201 msgs[1].addr = cmd->addr >> 1;
202 msgs[1].buf = (char *)cmd->data.bytes;
203 msgs[1].flags = I2C_M_RD;
204 msgs[1].len = cmd->data_len;
206 i2c_data.nmsgs = 2;
207 op = "read";
208 }
210 i2c_data.msgs = msgs;
211 ret = ioctl(i2c_file, I2C_RDWR, &i2c_data);
212 if (ret < 0) {
213 fprintf(stderr, "I2C %s failed at line %d error = %d\n",
214 op, cmd->line_num, ret);
215 return false;
216 }
218 return true;
219 }
221 static bool do_exec_cmd(int i2c_file, cmd_t *cmd)
222 {
223 uint8_t tmp_buf[CMD_MAX_DATA_SIZE];
225 switch (cmd->cmd_type) {
226 case CMD_R:
227 return i2c_rw(i2c_file, cmd, 0);
229 case CMD_W:
230 return i2c_rw(i2c_file, cmd, 1);
232 case CMD_C:
233 memcpy(tmp_buf, cmd->data.bytes, cmd->data_len);
234 if (!i2c_rw(i2c_file, cmd, 0))
235 return false;
236 if (memcmp(tmp_buf, cmd->data.bytes, cmd->data_len)) {
237 fprintf(stderr, "\nCommand C failed at line %d:\n",
238 cmd->line_num);
239 fprintf(stderr, "Expected data:");
240 print_buf(stderr, tmp_buf, cmd->data_len);
241 fprintf(stderr, "\nReceived data:");
242 print_buf(stderr, cmd->data.bytes, cmd->data_len);
243 fprintf(stderr, "\n");
244 return false;
245 }
246 return true;
248 case CMD_X:
249 usleep(cmd->data.delay * 1000);
250 return true;
252 default:
253 fprintf(stderr, "Unsupported command at line %d\n",
254 cmd->line_num);
255 return false;
256 }
257 }
259 static bool execute_cmd(int i2c_file, cmd_t *cmd)
260 {
261 int i = 1;
262 bool ret;
264 ret = do_exec_cmd(i2c_file, cmd);
266 while (!ret && i < RETRY_LIMIT) {
267 usleep(CMD_RETRY_DELAY * 1000);
268 ret = do_exec_cmd(i2c_file, cmd);
269 i++;
270 }
272 if (!ret) {
273 fprintf(stderr, "Command execution failed at line %d"
274 " addr - 0x%02x reg - 0x%02x, tried %d times\n",
275 cmd->line_num, cmd->addr, cmd->reg, RETRY_LIMIT);
276 }
278 return ret;
279 }
281 static bool get_delay(uint16_t *delay)
282 {
283 char *tok;
284 uint32_t temp;
286 tok = strtok(NULL, " ");
287 if (!tok)
288 return false; /*end of line or file */
290 if (1 != sscanf(tok, "%u", &temp)) {
291 fprintf(stderr, "Syntax error while parsing delay at line %d\n",
292 line_num);
293 return false; /* syntax error */
294 }
296 if (temp > UINT16_MAX) {
297 fprintf(stderr, "Command X delay too high at line %d - %dms\n",
298 line_num, temp);
299 return false;
300 }
302 *delay = (uint16_t)temp;
303 return true;
305 }
307 /*
308 * Returns:
309 * 0: success
310 * 1: EOF
311 * -1: Parse error
312 */
313 static int get_byte(uint8_t *byte)
314 {
315 char *tok;
316 unsigned int temp;
318 tok = strtok(NULL, " \t\r\n");
319 if (!tok)
320 return 1; /*end of line or file */
322 if ((strlen(tok) != 2) || (sscanf(tok, "%2x", &temp) != 1)) {
323 fprintf(stderr, "Syntax error at line %d, token - %s,"
324 " temp - %x\n", line_num, tok, temp);
325 return -1; /* syntax error */
326 }
328 *byte = (uint8_t)temp;
330 return 0; /* success */
331 }
333 static bool get_addr_n_reg(cmd_t *cmd)
334 {
335 if (get_byte(&cmd->addr))
336 return false;
338 if (get_byte(&cmd->reg))
339 return false;
341 return true;
342 }
344 static bool get_data_bytes(cmd_t *cmd)
345 {
346 int ret, i = 0;
347 cmd->data_len = 0;
349 do {
350 ret = get_byte(&cmd->data.bytes[i++]);
351 if (ret == -1)
352 return false;
353 } while ((ret == 0) && (i <= CMD_MAX_DATA_SIZE));
355 if (ret == 0) {
356 fprintf(stderr, "More than allowed number of data bytes at"
357 " line %d, data_len %d, i %d\n", cmd->line_num,
358 cmd->data_len, i);
359 return false;
360 }
362 cmd->data_len = i - 1;
364 return true;
365 }
367 static bool get_line(FILE *bqfs_file, char **buffer)
368 {
369 int c;
370 int i = 0;
371 bool ret = true;
372 char *buf;
374 buf = malloc(MAX_LINE_LEN);
375 line_num++;
377 while (1) {
378 c = fgetc(bqfs_file);
380 if (feof(bqfs_file)) {
381 #ifdef BQPROG_DEBUG
382 fprintf(stdout, "EOF\n");
383 #endif
384 break;
385 } else if (ferror(bqfs_file)) {
386 fprintf(stderr, "File read error\n");
387 ret = false;
388 break;
389 }
391 if (((c == '\r') || (c == '\n') || (c == '\t')
392 || (c == ' ')) && (i == 0)) {
393 /*
394 * Skip leading white space, if any, at the beginning
395 * of the line because this interferes with strtok
396 */
397 fprintf(stderr, "Leading whitespace at line %d\n",
398 line_num);
399 if (c == '\n')
400 line_num++;
401 continue; /* blank line, let's continue */
402 } else if (c == '\n') {
403 /* We've reached end of line */
404 break;
405 }
407 buf[i++] = c;
409 if (i == MAX_LINE_LEN) {
410 /*
411 * Re-allocate in case the line is longer than
412 * expected
413 */
414 buf = realloc(buf, MAX_LINE_LEN * 2);
415 fprintf(stderr, "Line %d longer than expected,"
416 " reallocating..\n", line_num);
417 } else if (i == MAX_LINE_LEN * 2) {
418 /*
419 * The line is already twice the expected maximum length
420 * - maybe the bqfs/dffs needs to be fixed
421 */
422 fprintf(stderr, "Line %d too long, abort parsing..\n",
423 line_num);
424 ret = false;
425 break;
426 }
427 }
429 *buffer = buf;
430 buf[i] = '\0';
432 if (i < 1)
433 ret = false;
435 return ret;
436 }
438 static bool get_cmd(FILE *bqfs_file, cmd_t *cmd)
439 {
440 char *res;
441 char *tok;
442 char *buf = NULL;
443 int ret;
445 if (!get_line(bqfs_file, &buf))
446 goto error;
448 cmd->line_num = line_num;
449 tok = strtok(buf, ":");
450 if (!tok || (strlen(tok) != 1)) {
451 fprintf(stderr, "Error parsing command at line %d tok=%s"
452 " buf=%s", line_num, tok, buf);
453 goto error;
454 }
456 switch (tok[0]) {
457 case 'R':
458 case 'r':
459 cmd->cmd_type = CMD_R;
460 if (!get_addr_n_reg(cmd))
461 goto error;
462 break;
463 case 'W':
464 case 'w':
465 cmd->cmd_type = CMD_W;
466 if (!get_addr_n_reg(cmd))
467 goto error;
468 if (!get_data_bytes(cmd))
469 goto error;
470 break;
471 case 'C':
472 case 'c':
473 cmd->cmd_type = CMD_C;
474 if (!get_addr_n_reg(cmd))
475 goto error;
476 if (!get_data_bytes(cmd))
477 goto error;
478 break;
479 case 'X':
480 case 'x':
481 cmd->cmd_type = CMD_X;
482 if (!get_delay(&cmd->data.delay))
483 goto error;
484 break;
485 default:
486 fprintf(stderr, "No command or unexpected command at"
487 " line %d tok=\"%s\" buf=\"%s\"",
488 line_num, tok, buf);
489 goto error;
490 }
492 print_cmd(cmd);
493 free(buf);
494 return true;
496 error:
497 cmd->cmd_type = CMD_INVALID;
498 free(buf);
499 return false;
500 }
502 int bqfs_flash(char *fname)
503 {
504 int poll_intvl = -1;
505 int i2c_file = -1;
506 int poll_file = -1;
507 FILE *bqfs_file = NULL;
508 char *line = NULL;
509 cmd_t *cmd = NULL;
510 int ret = 0;
511 char buf[20];
513 bqfs_file = fopen(fname, "r");
514 if (!bqfs_file) {
515 usage("--flash");
516 ret = -1;
517 goto end;
518 }
520 poll_intvl = read_bq_poll_intvl();
521 /* Turn off polling */
522 if (!write_bq_poll_intvl(0)) {
523 fprintf(stderr, "Failed to stop driver polling\n");
524 ret = -1;
525 goto end;
526 }
528 i2c_file = open(I2C_DEV, O_RDWR);
529 if (i2c_file < 0) {
530 fprintf(stderr, "Failed to open I2C device %s\n", I2C_DEV);
531 ret = -1;
532 goto end;
533 }
535 cmd = malloc(sizeof(cmd_t));
537 while (get_cmd(bqfs_file, cmd) && execute_cmd(i2c_file, cmd))
538 fputc('.', stdout);
540 if (feof(bqfs_file)) {
541 fprintf(stdout, "\n%s programmed successfully!\n", fname);
542 ret = 0;
543 } else {
544 fprintf(stdout, "\nprogramming %s failed!!\n", fname);
545 ret = -1;
546 }
548 end:
549 if (poll_intvl >= 0) {
550 if (!write_bq_poll_intvl(poll_intvl))
551 fprintf(stderr, "Failed to restore driver polling\n");
552 }
554 if (poll_file >= 0)
555 close(poll_file);
557 if (i2c_file >= 0)
558 close(i2c_file);
560 if (bqfs_file)
561 fclose(bqfs_file);
563 if (cmd)
564 free(cmd);
566 return ret;
567 }
569 int main(int argc, char **argv)
570 {
571 if ((argc >= 3) && (strncmp(argv[1], "--flash", 7) == 0)) {
572 return bqfs_flash(argv[2]);
573 } else {
574 usage(argv[0]);
575 return -1;
576 }
577 }