summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-06-06 12:16:11 -0700
committerGerrit <chrome-bot@google.com>2012-06-07 00:54:02 -0700
commitc7f2e0246e8129aa179b09c5042d0265266e3a17 (patch)
tree62a7e81c5119b9a8f09078dc85078a3396a005b8
parent3073c600786c6170cb3ff4d05ebc917f154c4ec1 (diff)
downloadchrome-ec-c7f2e0246e8129aa179b09c5042d0265266e3a17.tar.gz
Move printf() formatting to its own file to enable re-use
Also add snprintf(), and %X format code. BUG=chrome-os-partner:10206 TEST=timerinfo; should print correctly. 'ectool battery' on host side should print same serial as 'battery' on EC console. Change-Id: I5c9f69d1a20ee5d0a59440c122655adbf62c9aea Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/24635 Reviewed-by: Simon Glass <sjg@chromium.org> Reviewed-by: Rong Chang <rongchang@chromium.org>
-rw-r--r--common/build.mk2
-rw-r--r--common/charge_state.c9
-rw-r--r--common/printf.c236
-rw-r--r--common/uart_buffering.c221
-rw-r--r--include/console.h5
-rw-r--r--include/printf.h47
-rw-r--r--include/uart.h35
-rw-r--r--include/util.h5
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);