coreboot-libre-fam15h-rdimm/3rdparty/chromeec/common/usb_console_stream.c

234 lines
4.6 KiB
C

/* Copyright 2019 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 "crc.h"
#include "link_defs.h"
#include "printf.h"
#include "queue.h"
#include "task.h"
#include "timer.h"
#include "usb-stream.h"
#ifdef CONFIG_USB_CONSOLE
/*
* CONFIG_USB_CONSOLE and CONFIG_USB_CONSOLE_STREAM should be defined
* exclusively each other.
*/
#error "Do not enable CONFIG_USB_CONSOLE."
#endif
/* Console output macro */
#define USB_CONSOLE_TIMEOUT_US (30 * MSEC)
#define QUEUE_SIZE_USB_TX CONFIG_USB_CONSOLE_TX_BUF_SIZE
#define QUEUE_SIZE_USB_RX USB_MAX_PACKET_SIZE
static void usb_console_wr(struct queue_policy const *policy, size_t count);
static void uart_console_rd(struct queue_policy const *policy, size_t count);
static int last_tx_ok = 1;
/*
* Start enabled, so we can queue early debug output before the board gets
* around to calling usb_console_enable().
*/
static int is_enabled = 1;
/*
* But start read-only, so we don't accept console input until we explicitly
* decide that we're ready for it.
*/
static int is_readonly = 1;
/*
* This is a usb_console producer policy, which wakes up CONSOLE task whenever
* rx_q gets new data added. This shall be called by rx_stream_handler() in
* usb-stream.c.
*/
static struct queue_policy const usb_console_policy = {
.add = usb_console_wr,
.remove = uart_console_rd,
};
static struct queue const tx_q = QUEUE_NULL(QUEUE_SIZE_USB_TX, uint8_t);
static struct queue const rx_q = QUEUE(QUEUE_SIZE_USB_RX, uint8_t,
usb_console_policy);
struct usb_stream_config const usb_console;
USB_STREAM_CONFIG(usb_console,
USB_IFACE_CONSOLE,
USB_STR_CONSOLE_NAME,
USB_EP_CONSOLE,
USB_MAX_PACKET_SIZE,
USB_MAX_PACKET_SIZE,
rx_q,
tx_q)
static void usb_console_wr(struct queue_policy const *policy, size_t count)
{
console_has_input();
}
static void uart_console_rd(struct queue_policy const *policy, size_t count)
{
/* do nothing */
}
static void handle_output(void)
{
/* Wake up the Tx FIFO handler */
usb_console.consumer.ops->written(&usb_console.consumer, 1);
}
static int usb_wait_console(void)
{
timestamp_t deadline = get_time();
int wait_time_us = 1;
if (!is_enabled || !tx_fifo_is_ready(&usb_console))
return EC_SUCCESS;
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 (queue_space(&tx_q) < USB_MAX_PACKET_SIZE ||
!*usb_console.is_reset) {
if (timestamp_expired(deadline, NULL) ||
in_interrupt_context()) {
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;
}
} else {
last_tx_ok = queue_space(&tx_q);
}
return EC_SUCCESS;
}
#ifdef CONFIG_USB_CONSOLE_CRC
static uint32_t usb_tx_crc_ctx;
void usb_console_crc_init(void)
{
crc32_ctx_init(&usb_tx_crc_ctx);
}
uint32_t usb_console_crc(void)
{
return crc32_ctx_result(&usb_tx_crc_ctx);
}
#endif
static int __tx_char(void *context, int c)
{
int ret;
if (c == '\n') {
ret = __tx_char(NULL, '\r');
if (ret)
return ret;
}
#ifdef CONFIG_USB_CONSOLE_CRC
crc32_ctx_hash8(&usb_tx_crc_ctx, c);
while (queue_add_unit(&tx_q, &c) != 1)
usleep(500);
return EC_SUCCESS;
#else
/* Return 0 on success */
return queue_add_unit(&tx_q, &c) ? EC_SUCCESS : EC_ERROR_OVERFLOW;
#endif
}
/*
* Public USB console implementation below.
*/
int usb_getc(void)
{
int c;
if (is_readonly || !is_enabled)
return -1;
if (!queue_remove_unit(&rx_q, &c))
return -1;
return c;
}
int usb_puts(const char *outstr)
{
int ret;
if (!is_enabled)
return EC_SUCCESS;
ret = usb_wait_console();
if (ret)
return ret;
while (*outstr) {
ret = __tx_char(NULL, *outstr++);
if (ret)
break;
}
handle_output();
return ret;
}
int usb_putc(int c)
{
static char string[2] = { 0, '\0' };
string[0] = c;
return usb_puts(string);
}
int usb_vprintf(const char *format, va_list args)
{
int ret;
if (!is_enabled)
return EC_SUCCESS;
ret = usb_wait_console();
if (ret)
return ret;
ret = vfnprintf(__tx_char, NULL, format, args);
handle_output();
return ret;
}
void usb_console_enable(int enabled, int readonly)
{
is_enabled = enabled;
is_readonly = readonly;
}