diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2014-06-16 13:46:59 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-07-17 00:40:23 +0000 |
commit | a975c98fb2f378e4fc94cf73c38fe8afa8cb6eeb (patch) | |
tree | 6e12692cd15e1d06892a0cf15333e1e51c6146b0 | |
parent | 7746b32e17571b0e0cbdcbd101787b742d35c825 (diff) | |
download | chrome-ec-a975c98fb2f378e4fc94cf73c38fe8afa8cb6eeb.tar.gz |
usb: add USB console driver
Provide access to the EC console through 2 USB bulk endpoints.
(which can be used through the usbserial driver)
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
BRANCH=none
BUG=none
TEST=run on Fruitpie and use the console over USB
Change-Id: Ia897764f3a030972ee2ed323f293c5fca899765a
Reviewed-on: https://chromium-review.googlesource.com/204167
Reviewed-by: Anton Staaf <robotboy@chromium.org>
Commit-Queue: Vic Yang <victoryang@chromium.org>
Tested-by: Vic Yang <victoryang@chromium.org>
-rw-r--r-- | chip/stm32/build.mk | 1 | ||||
-rw-r--r-- | chip/stm32/usb_console.c | 245 | ||||
-rw-r--r-- | common/console.c | 39 | ||||
-rw-r--r-- | common/console_output.c | 31 | ||||
-rw-r--r-- | include/usb_console.h | 55 |
5 files changed, 356 insertions, 15 deletions
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index da9a54f363..d141fa2f60 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -39,5 +39,6 @@ chip-$(CONFIG_FLASH)+=flash-$(FLASH_FAMILY).o chip-$(CONFIG_ADC)+=adc-$(CHIP_FAMILY).o chip-$(CONFIG_PWM)+=pwm.o chip-$(CONFIG_USB)+=usb.o usb_endpoints.o +chip-$(CONFIG_USB_CONSOLE)+=usb_console.o chip-$(CONFIG_USB_HID)+=usb_hid.o chip-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_phy.o diff --git a/chip/stm32/usb_console.c b/chip/stm32/usb_console.c new file mode 100644 index 0000000000..a8b219bb87 --- /dev/null +++ b/chip/stm32/usb_console.c @@ -0,0 +1,245 @@ +/* Copyright (c) 2014 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. + */ + +#include "common.h" +#include "config.h" +#include "console.h" +#include "link_defs.h" +#include "printf.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "util.h" +#include "usb.h" + +/* Console output macro */ +#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) + +#define USB_CONSOLE_TIMEOUT_US (30 * MSEC) +#define USB_CONSOLE_RX_BUF_SIZE 16 +#define RX_BUF_NEXT(i) (((i) + 1) & (USB_CONSOLE_RX_BUF_SIZE - 1)) + +static volatile char rx_buf[USB_CONSOLE_RX_BUF_SIZE]; +static volatile int rx_buf_head; +static volatile int rx_buf_tail; + +static int last_tx_ok = 1; + +static int is_reset; + +/* USB-Serial descriptors */ +const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_CONSOLE) = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = USB_IFACE_CONSOLE, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, +}; +const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_CONSOLE, 82) = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x80 | USB_EP_CON_TX, + .bmAttributes = 0x02 /* Bulk IN */, + .wMaxPacketSize = USB_MAX_PACKET_SIZE, + .bInterval = 10 +}; +const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_CONSOLE, 3) = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_EP_CON_RX, + .bmAttributes = 0x02 /* Bulk OUT */, + .wMaxPacketSize = USB_MAX_PACKET_SIZE, + .bInterval = 0 +}; + +static usb_uint ep_buf_tx[USB_MAX_PACKET_SIZE / 2] __usb_ram; +static usb_uint ep_buf_rx[USB_MAX_PACKET_SIZE / 2] __usb_ram; + +static void con_ep_tx(void) +{ + uint16_t ep = STM32_USB_EP(USB_EP_CON_TX); + /* clear IT */ + STM32_USB_EP(USB_EP_CON_TX) = (ep & EP_MASK); + return; +} + +static void con_ep_rx(void) +{ + int i; + for (i = 0; i < (btable_ep[USB_EP_CON_RX].rx_count & 0x3ff); i++) { + int rx_buf_next = RX_BUF_NEXT(rx_buf_head); + if (rx_buf_next != rx_buf_tail) { + /* Not working on old STM32 ... */ + rx_buf[rx_buf_head] = ((uint8_t *)ep_buf_rx)[i]; + rx_buf_head = rx_buf_next; + } + } + /* clear IT */ + STM32_TOGGLE_EP(USB_EP_CON_RX, EP_RX_MASK, EP_RX_VALID, 0); + /* wake-up the console task */ + console_has_input(); + return; +} + +int usb_getc(void) +{ + int c; + + if (rx_buf_tail == rx_buf_head) + return -1; + + c = rx_buf[rx_buf_tail]; + rx_buf_tail = RX_BUF_NEXT(rx_buf_tail); + return c; +} + +static int __tx_char(void *context, int c) +{ + uint16_t *buf = (uint16_t *)ep_buf_tx; + int *tx_idx = context; + + /* Do newline to CRLF translation */ + if (c == '\n' && __tx_char(context, '\r')) + return 1; + + if (*tx_idx > 63) + return 1; + if (!(*tx_idx & 1)) + buf[*tx_idx/2] = c; + else + buf[*tx_idx/2] |= c << 8; + (*tx_idx)++; + + return 0; +} + +static void usb_enable_tx(int len) +{ + btable_ep[USB_EP_CON_TX].tx_count = len; + STM32_TOGGLE_EP(USB_EP_CON_TX, EP_TX_MASK, EP_TX_VALID, 0); +} + +static inline int usb_console_tx_valid(void) +{ + return (STM32_USB_EP(USB_EP_CON_TX) & EP_TX_MASK) == EP_TX_VALID; +} + +static int usb_wait_console(void) +{ + timestamp_t deadline = get_time(); + int wait_time_us = 1; + + deadline.val += USB_CONSOLE_TIMEOUT_US; + + /* + * If the USB console is not used, Tx buffer would never free up. + * In this case, let's drop characters immediately instead of sitting + * for some time just to time out. On the other hand, if the last + * Tx is good, it's likely the host is there to receive data, and + * we should wait so that we don't clobber the buffer. + */ + if (last_tx_ok) { + while (usb_console_tx_valid() || !is_reset) { + if (timestamp_expired(deadline, NULL)) { + last_tx_ok = 0; + return EC_ERROR_TIMEOUT; + } + if (wait_time_us < MSEC) + udelay(wait_time_us); + else + usleep(wait_time_us); + wait_time_us *= 2; + } + + return EC_SUCCESS; + } else { + last_tx_ok = !usb_console_tx_valid(); + return EC_SUCCESS; + } +} + +int usb_putc(int c) +{ + int ret; + int tx_idx = 0; + + ret = usb_wait_console(); + if (ret) + return ret; + + ret = __tx_char(&tx_idx, c); + usb_enable_tx(tx_idx); + + return ret; +} + +int usb_puts(const char *outstr) +{ + int ret; + int tx_idx = 0; + + ret = usb_wait_console(); + if (ret) + return ret; + + /* Put all characters in the output buffer */ + while (*outstr) { + if (__tx_char(&tx_idx, *outstr++) != 0) + break; + } + + usb_enable_tx(tx_idx); + + /* Successful if we consumed all output */ + return *outstr ? EC_ERROR_OVERFLOW : EC_SUCCESS; +} + +int usb_vprintf(const char *format, va_list args) +{ + int ret; + int tx_idx = 0; + + ret = usb_wait_console(); + if (ret) + return ret; + + ret = vfnprintf(__tx_char, &tx_idx, format, args); + + usb_enable_tx(tx_idx); + return ret; +} + +static void ep_tx_reset(void) +{ + /* Serial Bulk IN endpoint 2 */ + btable_ep[USB_EP_CON_TX].tx_addr = usb_sram_addr(ep_buf_tx); + btable_ep[USB_EP_CON_TX].tx_count = 0; + btable_ep[USB_EP_CON_TX].rx_count = 0; + STM32_USB_EP(USB_EP_CON_TX) = (USB_EP_CON_TX << 0) /* Endpoint Addr*/ | + (2 << 4) /* TX NAK */ | + (0 << 9) /* Bulk EP */ | + (0 << 12) /* RX Disabled */; + is_reset = 1; +} + +static void ep_rx_reset(void) +{ + /* Serial Bulk OUT endpoint 3 */ + btable_ep[USB_EP_CON_RX].rx_addr = usb_sram_addr(ep_buf_rx); + btable_ep[USB_EP_CON_RX].tx_count = 0; + btable_ep[USB_EP_CON_RX].rx_count = + 0x8000 | ((USB_MAX_PACKET_SIZE / 32 - 1) << 10); + STM32_USB_EP(USB_EP_CON_RX) = (USB_EP_CON_RX << 0) /* Endpoint Addr */ | + (0 << 4) /* TX Disabled */ | + (0 << 9) /* Bulk EP */ | + (3 << 12) /* RX VALID */; +} + +USB_DECLARE_EP(USB_EP_CON_TX, con_ep_tx, con_ep_tx, ep_tx_reset); +USB_DECLARE_EP(USB_EP_CON_RX, con_ep_rx, con_ep_rx, ep_rx_reset); diff --git a/common/console.c b/common/console.c index 2920db60fc..20ec4ce50f 100644 --- a/common/console.c +++ b/common/console.c @@ -4,12 +4,14 @@ */ /* Console module for Chrome EC */ + #include "clock.h" #include "console.h" #include "link_defs.h" #include "system.h" #include "task.h" #include "uart.h" +#include "usb_console.h" #include "util.h" #define MAX_ARGS_PER_COMMAND 10 @@ -205,6 +207,14 @@ static void console_init(void) ccputs(PROMPT); } +static int console_putc(int c) +{ + int rv1 = uart_putc(c); + int rv2 = usb_putc(c); + + return rv1 == EC_SUCCESS ? rv2 : rv1; +} + static void move_cursor_right(void) { if (input_pos == input_len) @@ -244,7 +254,7 @@ static void move_cursor_begin(void) static void repeat_char(char c, int cnt) { while (cnt--) - uart_putc(c); + console_putc(c); } #ifdef CONFIG_CONSOLE_HISTORY @@ -289,7 +299,7 @@ static void handle_backspace(void) return; /* Already at beginning of line */ /* Move cursor back */ - uart_putc('\b'); + console_putc('\b'); /* Print and move anything following the cursor position */ if (input_pos != input_len) { @@ -302,7 +312,7 @@ static void handle_backspace(void) } /* Space over last character and move cursor to correct position */ - uart_putc(' '); + console_putc(' '); repeat_char('\b', input_len - input_pos + 1); input_len--; @@ -413,7 +423,7 @@ static void console_handle_char(int c) case '\n': /* Terminate this line */ - uart_puts("\r\n"); + console_putc('\n'); #ifdef CONFIG_CONSOLE_HISTORY /* Save command in history buffer */ @@ -512,7 +522,7 @@ static void console_handle_char(int c) break; /* Print character */ - uart_putc(c); + console_putc(c); /* If not at end of line, print rest of line and move it down */ if (input_pos != input_len) { @@ -554,12 +564,23 @@ void console_task(void) console_init(); while (1) { - int c = uart_getc(); + int c; + + while (1) { + c = uart_getc(); + if (c == -1) + break; + console_handle_char(c); + } - if (c == -1) - task_wait_event(-1); /* Wait for more input */ - else + while (1) { + c = usb_getc(); + if (c == -1) + break; console_handle_char(c); + } + + task_wait_event(-1); /* Wait for more input */ } } diff --git a/common/console_output.c b/common/console_output.c index 3bbad84ad4..bc28a8a3b1 100644 --- a/common/console_output.c +++ b/common/console_output.c @@ -7,6 +7,7 @@ #include "console.h" #include "uart.h" +#include "usb_console.h" #include "util.h" /* Default to all channels active */ @@ -68,26 +69,36 @@ BUILD_ASSERT(ARRAY_SIZE(channel_names) == CC_CHANNEL_COUNT); int cputs(enum console_channel channel, const char *outstr) { + int rv1, rv2; + /* Filter out inactive channels */ if (!(CC_MASK(channel) & channel_mask)) return EC_SUCCESS; - return uart_puts(outstr); + rv1 = usb_puts(outstr); + rv2 = uart_puts(outstr); + + return rv1 == EC_SUCCESS ? rv2 : rv1; } int cprintf(enum console_channel channel, const char *format, ...) { - int rv; + int rv1, rv2; va_list args; /* Filter out inactive channels */ if (!(CC_MASK(channel) & channel_mask)) return EC_SUCCESS; + usb_va_start(args, format); + rv1 = usb_vprintf(format, args); + usb_va_end(args); + va_start(args, format); - rv = uart_vprintf(format, args); + rv2 = uart_vprintf(format, args); va_end(args); - return rv; + + return rv1 == EC_SUCCESS ? rv2 : rv1; } int cprints(enum console_channel channel, const char *format, ...) @@ -99,13 +110,21 @@ int cprints(enum console_channel channel, const char *format, ...) if (!(CC_MASK(channel) & channel_mask)) return EC_SUCCESS; + rv = cprintf(channel, "[%T "); + va_start(args, format); - rv = uart_printf("[%T "); r = uart_vprintf(format, args); if (r) rv = r; - r = uart_puts("]\n"); va_end(args); + + usb_va_start(args, format); + r = usb_vprintf(format, args); + if (r) + rv = r; + usb_va_end(args); + + r = cputs(channel, "]\n"); return r ? r : rv; } diff --git a/include/usb_console.h b/include/usb_console.h new file mode 100644 index 0000000000..9666fa425a --- /dev/null +++ b/include/usb_console.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2014 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. + */ + +/* USB serial console module */ + +#ifndef __USB_CONSOLE_H +#define __USB_CONSOLE_H + +#ifdef CONFIG_USB_CONSOLE + +/** + * Put a null-terminated string to the USB console, like fputs(). + * + * @return EC_SUCCESS, or non-zero if output was truncated. + */ +int usb_puts(const char *outstr); + +/** + * Print formatted output to the USB console, like vprintf(). + * + * See printf.h for valid formatting codes. + * + * @return EC_SUCCESS, or non-zero if output was truncated. + */ +int usb_vprintf(const char *format, va_list args); + +/** + * Put a single character to the USB console, like putchar(). + * + * @param c Character to put + * @return EC_SUCCESS, or non-zero if output was truncated. + */ +int usb_putc(int c); + +/** + * Read a single character of input, similar to fgetc(). + * + * @return the character, or -1 if no input waiting. + */ +int usb_getc(void); + +#define usb_va_start va_start +#define usb_va_end va_end +#else +#define usb_puts(x) EC_SUCCESS +#define usb_vprintf(x, y) EC_SUCCESS +#define usb_putc(x) EC_SUCCESS +#define usb_getc(x) (-1) +#define usb_va_start(x, y) +#define usb_va_end(x) +#endif + +#endif /* __USB_CONSOLE_H */ |