diff options
-rw-r--r-- | common/build.mk | 2 | ||||
-rw-r--r-- | common/charge_state.c | 9 | ||||
-rw-r--r-- | common/printf.c | 236 | ||||
-rw-r--r-- | common/uart_buffering.c | 221 | ||||
-rw-r--r-- | include/console.h | 5 | ||||
-rw-r--r-- | include/printf.h | 47 | ||||
-rw-r--r-- | include/uart.h | 35 | ||||
-rw-r--r-- | include/util.h | 5 |
8 files changed, 350 insertions, 210 deletions
diff --git a/common/build.mk b/common/build.mk index 223769f363..f32e945113 100644 --- a/common/build.mk +++ b/common/build.mk @@ -8,7 +8,7 @@ common-y=main.o util.o console.o uart_buffering.o common-y+=memory_commands.o shared_mem.o system_common.o hooks.o -common-y+=gpio_commands.o version.o +common-y+=gpio_commands.o version.o printf.o common-$(CONFIG_BATTERY_ATL706486)+=battery_atl706486.o common-$(CONFIG_CHARGER_BQ24725)+=charger_bq24725.o common-$(CONFIG_PMU_TPS65090)+=pmu_tps65090.o diff --git a/common/charge_state.c b/common/charge_state.c index a519de17c5..775a784cb3 100644 --- a/common/charge_state.c +++ b/common/charge_state.c @@ -18,6 +18,7 @@ #include "ec_commands.h" #include "power_button.h" #include "power_led.h" +#include "printf.h" #include "smart_battery.h" #include "system.h" #include "timer.h" @@ -72,12 +73,8 @@ static void update_battery_info(void) /* Smart battery serial number is 16 bits */ batt_str = (char *)(lpc_get_memmap_range() + EC_MEMMAP_BATT_SERIAL); memset(batt_str, 0, EC_MEMMAP_TEXT_MAX); - if (battery_serial_number(&batt_serial) == 0) { - *batt_str++ = hex2asc(0xf & (batt_serial >> 12)); - *batt_str++ = hex2asc(0xf & (batt_serial >> 8)); - *batt_str++ = hex2asc(0xf & (batt_serial >> 4)); - *batt_str++ = hex2asc(0xf & batt_serial); - } + if (battery_serial_number(&batt_serial) == 0) + snprintf(batt_str, EC_MEMMAP_TEXT_MAX, "%04X", batt_serial); } /* Prevent battery from going into deep discharge state */ diff --git a/common/printf.c b/common/printf.c new file mode 100644 index 0000000000..af393daa0b --- /dev/null +++ b/common/printf.c @@ -0,0 +1,236 @@ +/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Printf-like functionality for Chrome EC */ + +#include "printf.h" +#include "timer.h" +#include "util.h" + +static const char error_str[] = "ERROR"; + +int vfnprintf(int (*addchar)(void *context, int c), void *context, + const char *format, va_list args) +{ + char intbuf[34]; + /* Longest uint64 in decimal = 20 + * longest uint32 in binary = 32 + */ + int dropped_chars = 0; + int is_left; + int pad_zero; + int pad_width; + char *vstr; + int vlen; + + while (*format && !dropped_chars) { + int c = *format++; + + /* Copy normal characters */ + if (c != '%') { + dropped_chars |= addchar(context, c); + continue; + } + + /* Get first format character */ + c = *format++; + + /* Send "%" for "%%" input */ + if (c == '%' || c == '\0') { + dropped_chars |= addchar(context, '%'); + continue; + } + + /* Handle %c */ + if (c == 'c') { + c = va_arg(args, int); + dropped_chars |= addchar(context, c); + continue; + } + + /* Handle left-justification ("%-5s") */ + is_left = (c == '-'); + if (is_left) + c = *format++; + + /* Handle padding with 0's */ + pad_zero = (c == '0'); + if (pad_zero) + c = *format++; + + /* Count padding length */ + pad_width = 0; + while (c >= '0' && c <= '9') { + pad_width = (10 * pad_width) + c - '0'; + c = *format++; + } + if (pad_width > 80) { + /* Sanity check for width failed */ + format = error_str; + continue; + } + + if (c == 's') { + vstr = va_arg(args, char *); + if (vstr == NULL) + vstr = "(NULL)"; + } else { + uint64_t v; + int is_negative = 0; + int is_64bit = 0; + int base = 10; + int fixed_point = 0; + + /* Handle fixed point numbers */ + if (c == '.') { + c = *format++; + if (c < '0' || c > '9') { + format = error_str; + continue; + } + fixed_point = c - '0'; + c = *format++; + } + + if (c == 'l') { + is_64bit = 1; + c = *format++; + } + + /* Special-case: %T = current time */ + if (c == 'T') { + v = get_time().val; + is_64bit = 1; + fixed_point = 6; + } else if (is_64bit) { + v = va_arg(args, uint64_t); + } else { + v = va_arg(args, uint32_t); + } + + switch (c) { + case 'd': + if (is_64bit) { + if ((int64_t)v < 0) { + is_negative = 1; + if (v != (1ULL << 63)) + v = -v; + } + } else { + if ((int)v < 0) { + is_negative = 1; + if (v != (1ULL << 31)) + v = -(int)v; + } + } + break; + case 'u': + case 'T': + break; + case 'X': + case 'x': + case 'p': + base = 16; + break; + case 'b': + base = 2; + break; + default: + format = error_str; + } + if (format == error_str) + continue; /* Bad format specifier */ + + /* Convert integer to string, starting at end of + * buffer and working backwards. */ + vstr = intbuf + sizeof(intbuf) - 1; + *(vstr) = '\0'; + + /* Handle digits to right of decimal for fixed point + * numbers. */ + for (vlen = 0; vlen < fixed_point; vlen++) + *(--vstr) = '0' + uint64divmod(&v, 10); + if (fixed_point) + *(--vstr) = '.'; + + if (!v) + *(--vstr) = '0'; + + while (v) { + int digit = uint64divmod(&v, base); + if (digit < 10) + *(--vstr) = '0' + digit; + else if (c == 'X') + *(--vstr) = 'A' + digit - 9; + else + *(--vstr) = 'a' + digit - 9; + } + + if (is_negative) + *(--vstr) = '-'; + } + + /* Copy string (or stringified integer) */ + vlen = strlen(vstr); + while (vlen < pad_width && !is_left) { + dropped_chars |= addchar(context, pad_zero ? '0' : ' '); + vlen++; + } + while (*vstr) + dropped_chars |= addchar(context, *vstr++); + while (vlen < pad_width && is_left) { + dropped_chars |= addchar(context, ' '); + vlen++; + } + } + + /* Successful if we consumed all output */ + return dropped_chars ? EC_ERROR_OVERFLOW : EC_SUCCESS; +} + + +/* Context for snprintf() */ +struct snprintf_context { + char *str; + int size; +}; + + +/* Add a character to the string */ +static int snprintf_addchar(void *context, int c) +{ + struct snprintf_context *ctx = (struct snprintf_context *)context; + + if (!ctx->size) + return 1; + + *(ctx->str++) = c; + ctx->size--; + return 0; +} + + +/* Print formatted outut to a string */ +int snprintf(char *str, int size, const char *format, ...) +{ + struct snprintf_context ctx; + va_list args; + int rv; + + if (!str || !size) + return EC_ERROR_INVAL; + + ctx.str = str; + ctx.size = size - 1; /* Reserve space for terminating '\0' */ + + va_start(args, format); + rv = vfnprintf(snprintf_addchar, &ctx, format, args); + va_end(args); + + /* Terminate string */ + *ctx.str = '\0'; + + return rv; +} diff --git a/common/uart_buffering.c b/common/uart_buffering.c index 0aa3c760b7..9a0fd715ed 100644 --- a/common/uart_buffering.c +++ b/common/uart_buffering.c @@ -9,9 +9,9 @@ #include "console.h" #include "task.h" -#include "timer.h" #include "uart.h" #include "util.h" +#include "printf.h" /* Buffer sizes; should be power of 2 */ #define TX_BUF_SIZE 512 @@ -58,19 +58,17 @@ static volatile int cmd_history_ptr; static int console_mode = 1; -/* TODO: should have an API to set raw mode for the UART. In raw - * mode, we don't do CRLF translation or echo input. */ - /* Put a single character into the transmit buffer. Does not enable * the transmit interrupt; assumes that happens elsewhere. Returns - * zero if the character was transmitted, 1 if it was dropped. */ -static int __tx_char(int c) + * zero if the character was transmitted, 1 if it was dropped. We only + * have a single transmit buffer, so context is ignored. */ +static int __tx_char(void *context, int c) { int tx_buf_next; /* Do newline to CRLF translation */ - if (console_mode && c == '\n' && __tx_char('\r')) + if (console_mode && c == '\n' && __tx_char(NULL, '\r')) return 1; tx_buf_next = TX_BUF_NEXT(tx_buf_head); @@ -82,6 +80,7 @@ static int __tx_char(int c) return 0; } + static void move_rx_ptr_fwd(void) { if (rx_cur_buf_ptr != rx_cur_buf_head) { @@ -93,6 +92,7 @@ static void move_rx_ptr_fwd(void) } } + static void move_rx_ptr_bwd(void) { if (rx_cur_buf_ptr != 0) { @@ -104,12 +104,14 @@ static void move_rx_ptr_bwd(void) } } + static void repeat_char(char c, int cnt) { while (cnt--) uart_write_char(c); } + static void handle_backspace(void) { if (rx_cur_buf_ptr != 0) { @@ -134,6 +136,7 @@ static void handle_backspace(void) uart_write_char(' '); } + static void insert_char(char c) { int ptr; @@ -169,6 +172,7 @@ static void insert_char(char c) } } + static int rx_buf_space_available(void) { if (cmd_history_head == cmd_history_tail) @@ -177,6 +181,7 @@ static int rx_buf_space_available(void) cmd_history[CMD_HIST_PREV(cmd_history_head)].head); } + static void history_save(void) { int ptr; @@ -211,6 +216,7 @@ static void history_save(void) cmd_history[hist_id].tail = tail; } + static void history_load(int id) { int head = cmd_history[id].head; @@ -238,6 +244,7 @@ static void history_load(int id) rx_cur_buf_head = rx_cur_buf_ptr; } + static void history_prev(void) { if (cmd_history_ptr == cmd_history_tail) @@ -260,6 +267,7 @@ static void history_prev(void) history_load(cmd_history_ptr); } + static void history_next(void) { if (cmd_history_ptr == cmd_history_head) @@ -273,6 +281,7 @@ static void history_next(void) cmd_history_head = cmd_history_ptr; } + /* Helper for UART processing */ void uart_process(void) { @@ -371,7 +380,7 @@ int uart_puts(const char *outstr) { /* Put all characters in the output buffer */ while (*outstr) { - if (__tx_char(*outstr++) != 0) + if (__tx_char(NULL, *outstr++) != 0) break; } @@ -385,190 +394,56 @@ int uart_puts(const char *outstr) int uart_vprintf(const char *format, va_list args) { - static const char int_chars[] = "0123456789abcdef"; - static const char error_str[] = "ERROR"; - char intbuf[34]; - /* Longest uint64 in decimal = 20 - * longest uint32 in binary = 32 - */ - int dropped_chars = 0; - int is_left; - int pad_zero; - int pad_width; - char *vstr; - int vlen; - - while (*format && !dropped_chars) { - int c = *format++; - - /* Copy normal characters */ - if (c != '%') { - dropped_chars |= __tx_char(c); - continue; - } - - /* Get first format character */ - c = *format++; - - /* Send "%" for "%%" input */ - if (c == '%' || c == '\0') { - dropped_chars |= __tx_char('%'); - continue; - } - - /* Handle %c */ - if (c == 'c') { - c = va_arg(args, int); - dropped_chars |= __tx_char(c); - continue; - } - - /* Handle left-justification ("%-5s") */ - is_left = (c == '-'); - if (is_left) - c = *format++; - - /* Handle padding with 0's */ - pad_zero = (c == '0'); - if (pad_zero) - c = *format++; - - /* Count padding length */ - pad_width = 0; - while (c >= '0' && c <= '9') { - pad_width = (10 * pad_width) + c - '0'; - c = *format++; - } - if (pad_width > 80) { - /* Sanity check for width failed */ - format = error_str; - continue; - } - - if (c == 's') { - vstr = va_arg(args, char *); - if (vstr == NULL) - vstr = "(NULL)"; - } else { - uint64_t v; - int is_negative = 0; - int is_64bit = 0; - int base = 10; - int fixed_point = 0; - - /* Handle fixed point numbers */ - if (c == '.') { - c = *format++; - if (c < '0' || c > '9') { - format = error_str; - continue; - } - fixed_point = c - '0'; - c = *format++; - } - - if (c == 'l') { - is_64bit = 1; - c = *format++; - } - - /* Special-case: %T = current time */ - if (c == 'T') { - v = get_time().val; - is_64bit = 1; - fixed_point = 6; - } else if (is_64bit) { - v = va_arg(args, uint64_t); - } else { - v = va_arg(args, uint32_t); - } + int rv = vfnprintf(__tx_char, NULL, format, args); - switch (c) { - case 'd': - if (is_64bit) { - if ((int64_t)v < 0) { - is_negative = 1; - if (v != (1ULL << 63)) - v = -v; - } - } else { - if ((int)v < 0) { - is_negative = 1; - if (v != (1ULL << 31)) - v = -(int)v; - } - } - break; - case 'u': - case 'T': - break; - case 'x': - case 'p': - base = 16; - break; - case 'b': - base = 2; - break; - default: - format = error_str; - } - if (format == error_str) - continue; /* Bad format specifier */ + if (uart_tx_stopped()) + uart_tx_start(); - /* Convert integer to string, starting at end of - * buffer and working backwards. */ - vstr = intbuf + sizeof(intbuf) - 1; - *(vstr) = '\0'; + return rv; +} - /* Handle digits to right of decimal for fixed point - * numbers. */ - for (vlen = 0; vlen < fixed_point; vlen++) - *(--vstr) = int_chars[uint64divmod(&v, 10)]; - if (fixed_point) - *(--vstr) = '.'; - if (!v) - *(--vstr) = '0'; +int uart_printf(const char *format, ...) +{ + int rv; + va_list args; - while (v) - *(--vstr) = int_chars[uint64divmod(&v, base)]; + va_start(args, format); + rv = uart_vprintf(format, args); + va_end(args); + return rv; +} - if (is_negative) - *(--vstr) = '-'; - } - /* Copy string (or stringified integer) */ - vlen = strlen(vstr); - while (vlen < pad_width && !is_left) { - dropped_chars |= __tx_char(pad_zero ? '0' : ' '); - vlen++; - } - while (*vstr) - dropped_chars |= __tx_char(*vstr++); - while (vlen < pad_width && is_left) { - dropped_chars |= __tx_char(' '); - vlen++; - } - } - - if (uart_tx_stopped()) - uart_tx_start(); +/* Add a character directly to the UART buffer */ +static int emergency_txchar(void *format, int c) +{ + /* Wait for space */ + while (!uart_tx_ready()) + ; - /* Successful if we consumed all output */ - return dropped_chars ? EC_ERROR_OVERFLOW : EC_SUCCESS; + /* Write the character */ + uart_write_char(c); + return 0; } -int uart_printf(const char *format, ...) +int uart_emergency_printf(const char *format, ...) { int rv; va_list args; va_start(args, format); - rv = uart_vprintf(format, args); + rv = vfnprintf(emergency_txchar, NULL, format, args); va_end(args); + + /* Wait for transmit FIFO empty */ + uart_tx_flush(); + return rv; } + + /* For use when debugging verified boot. We could wrap it with a real function, * but it's rarely needed and this doesn't add any extra code. We have to * declare it here in order for this trick to work. */ diff --git a/include/console.h b/include/console.h index f0d131249d..37d44bfc55 100644 --- a/include/console.h +++ b/include/console.h @@ -63,8 +63,9 @@ enum console_channel { /* Put a string to the console channel. */ int cputs(enum console_channel channel, const char *outstr); -/* Print formatted output to the console channel. See uart_vprintf() for - * valid format codes. */ +/* Print formatted output to the console channel. + * + * See printf.h for valid formatting codes. */ int cprintf(enum console_channel channel, const char *format, ...); /* Flush the console output for all channels. */ diff --git a/include/printf.h b/include/printf.h new file mode 100644 index 0000000000..66e8d0c8b5 --- /dev/null +++ b/include/printf.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Printf-like functionality for Chrome EC */ + +#ifndef __CROS_EC_PRINTF_H +#define __CROS_EC_PRINTF_H + +#include <stdarg.h> /* For va_list */ +#include "common.h" + +/* SUPPORTED FORMAT CODES: + * char (%c) + * string (%s) + * native int (signed/unsigned) (%d / %u / %x / %X) + * int32_t / uint32_t (%d / %x / %X) + * int64_t / uint64_t (%ld / %lu / %lx / %lX) + * pointer (%p) + * And the following special format codes: + * current time in sec (%T) - interpreted as "%.6T" for fixed-point format + * including padding (%-5s, %8d, %08x, %016lx) + * + * Floating point output (%f / %g) is not supported, but there is a fixed-point + * extension for integers; a padding option of .N (where N is a number) will + * put a decimal before that many digits. For example, printing 123 with + * format code %.6d will result in "0.000123". This is most useful for + * printing times, voltages, and currents. */ + + +/* Print formatted output to a function, like vfprintf() + * + * addchar() will be called for every character to be printed, with the context + * pointer passed to vfnprintf(). addchar() should return 0 if the character + * was accepted or non-zero if the character was dropped due to overflow. + * + * Returns error if output was truncated. */ +int vfnprintf(int (*addchar)(void *context, int c), void *context, + const char *format, va_list args); + + +/* Print formatted outut to a string */ +int snprintf(char *str, int size, const char *format, ...); + + +#endif /* __CROS_EC_PRINTF_H */ diff --git a/include/uart.h b/include/uart.h index f71d151c76..27293f7b0c 100644 --- a/include/uart.h +++ b/include/uart.h @@ -40,28 +40,12 @@ int uart_puts(const char *outstr); /* Print formatted output to the UART, like printf(). * - * Returns error if output was truncated. - * - * Supports the following format strings: - * char (%c) - * string (%s) - * native int (signed/unsigned) (%d / %u / %x) - * int32_t / uint32_t (%d / %x) - * int64_t / uint64_t (%ld / %lu / %lx) - * pointer (%p) - * And the following special format codes: - * current time in sec (%T) - interpreted as "%.6T" for fixed-point format - * including padding (%-5s, %8d, %08x, %016lx) - * - * Floating point output (%f / %g) is not supported, but there is a fixed-point - * extension for integers; a padding option of .N (where N is a number) will - * put a decimal before that many digits. For example, printing 123 with - * format code %.6d will result in "0.000123". This is most useful for - * printing times, voltages, and currents. */ + * See printf.h for valid formatting codes. */ int uart_printf(const char *format, ...); -/* Print formatted output to the UART, like vprintf(). Supports the same - * formatting codes as uart_printf(). */ +/* Print formatted output to the UART, like vprintf(). + * + * See printf.h for valid formatting codes. */ int uart_vprintf(const char *format, va_list args); /* Flushes output. Blocks until UART has transmitted all output. */ @@ -69,11 +53,16 @@ void uart_flush_output(void); /* Flushes output. * - * Blocks until UART has transmitted all output, - * even if we are in high priority interrupt context - */ + * Blocks until UART has transmitted all output, even in a high priority + * interrupt context. */ void uart_emergency_flush(void); +/* Like uart_printf(), but bypasses the transmit buffer. + * + * Blocks until UART has transmitted the formatted output, even in a high + * priority interrupt context. */ +int uart_emergency_printf(const char *format, ...); + /*****************************************************************************/ /* Input functions * diff --git a/include/util.h b/include/util.h index 68e4668f03..cf1ffde4d7 100644 --- a/include/util.h +++ b/include/util.h @@ -52,11 +52,6 @@ #define DIV_ROUND_UP(x, y) (((x) + ((y) - 1)) / (y)) #define DIV_ROUND_NEAREST(x, y) (((x) + ((y) / 2)) / (y)) -static inline uint8_t hex2asc(uint8_t hex) -{ - return hex + ((hex > 9) ? 'A' : '0'); -} - /* Standard library functions */ int atoi(const char *nptr); int isdigit(int c); |