/* Copyright 2016 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. */ #ifdef BOARD_CR50 #include "ec_comm.h" #endif #include "queue.h" #include "queue_policies.h" #ifdef CONFIG_STREAM_SIGNATURE #include "signing.h" #endif #ifdef CONFIG_UART_BITBANG #include "uart_bitbang.h" #endif #include "uartn.h" #include "usart.h" #include "usb-stream.h" #define USE_UART_INTERRUPTS (!(defined(CONFIG_CUSTOMIZED_RO) && \ defined(SECTION_IS_RO))) #define QUEUE_SIZE 64 /* * Want to be able to accumulate larger amounts of data while USB is * momentarily stalled for whatever reason. */ #define QUEUE_SIZE_UART_RX 512 #ifdef CONFIG_STREAM_SIGNATURE /* * When signing over streaming data, up the relevant queue sizes. */ #define QUEUE_SIZE_SIG_IN 1024 #define QUEUE_SIZE_USB_IN 8192 #define QUEUE_SIZE_UART_IN 1024 #else #define QUEUE_SIZE_SIG_IN QUEUE_SIZE #define QUEUE_SIZE_USB_IN QUEUE_SIZE #define QUEUE_SIZE_UART_IN QUEUE_SIZE #endif #ifdef CONFIG_STREAM_USART1 struct usb_stream_config const ap_usb; struct usart_config const ap_uart; #ifdef CONFIG_STREAM_SIGNATURE /* * This code adds the ability to capture UART data received, and * sign it with H1's key. This allows the log output to be verified * as actual UART output from this board. * * This functionality is enabled by redirecting the UART receive queue * to feed into the signing module rather than the usb tx. After being * added to the running hash, the data is then pushed by the signer * into the usb tx queue. */ struct signer_config const sig; static struct queue const ap_uart_output = QUEUE_DIRECT(QUEUE_SIZE_SIG_IN, uint8_t, ap_uart.producer, sig.consumer); static struct queue const sig_to_usb = QUEUE_DIRECT(QUEUE_SIZE_USB_IN, uint8_t, sig.producer, ap_usb.consumer); SIGNER_CONFIG(sig, stream_uart, sig_to_usb, ap_uart_output); #else /* Not CONFIG_STREAM_SIGNATURE */ static struct queue const ap_uart_output = QUEUE_DIRECT(QUEUE_SIZE_UART_RX, uint8_t, ap_uart.producer, ap_usb.consumer); #endif static struct queue const ap_usb_to_uart = QUEUE_DIRECT(QUEUE_SIZE_UART_IN, uint8_t, ap_usb.producer, ap_uart.consumer); /* * AP UART data is sent to the ap_uart_output queue, and received from * the ap_usb_to_uart queue. The ap_uart_output queue is received by the * USB bridge, or if a signer is enabled, received by the signer, which then * passes the data to the USB bridge after processing it. */ USART_CONFIG(ap_uart, UART_AP, ap_uart_output, ap_usb_to_uart); /* * The UART USB bridge receives character data from the UART's queue, * unless signing is enabled, in which case it receives data from the * signer's queue, after the signer has received it from the UART and * processed it. */ USB_STREAM_CONFIG(ap_usb, USB_IFACE_AP, USB_STR_AP_NAME, USB_EP_AP, USB_MAX_PACKET_SIZE, USB_MAX_PACKET_SIZE, ap_usb_to_uart, #ifdef CONFIG_STREAM_SIGNATURE sig_to_usb) #else ap_uart_output) #endif #endif /* CONFIG_STREAM_USART1 */ #ifdef CONFIG_STREAM_USART2 struct usb_stream_config const ec_usb; struct usart_config const ec_uart; static struct queue const ec_uart_to_usb = QUEUE_DIRECT(QUEUE_SIZE_UART_RX, uint8_t, ec_uart.producer, ec_usb.consumer); static struct queue const ec_usb_to_uart = QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ec_usb.producer, ec_uart.consumer); USART_CONFIG(ec_uart, UART_EC, ec_uart_to_usb, ec_usb_to_uart); USB_STREAM_CONFIG(ec_usb, USB_IFACE_EC, USB_STR_EC_NAME, USB_EP_EC, USB_MAX_PACKET_SIZE, USB_MAX_PACKET_SIZE, ec_usb_to_uart, ec_uart_to_usb) #endif #ifdef BOARD_CR50 static uint8_t ec_bridge_enabled_; static uint8_t ec_bridge_tx_enabled_; void uart_ec_bridge_enable(int enable, int write) { write = enable && write; if (write && !ec_bridge_tx_enabled_) task_trigger_irq(GC_IRQNUM_UART2_TXINT); ec_bridge_enabled_ = enable; ec_bridge_tx_enabled_ = write; } int uart_ec_bridge_is_enabled(void) { return !!ec_bridge_enabled_; } int uart_ec_bridge_tx_is_enabled(void) { return !!ec_bridge_tx_enabled_; } #endif /* BOARD_CR50 */ void get_data_from_usb(struct usart_config const *config) { struct queue const *uart_out = config->consumer.queue; int c; /* Copy output from buffer until TX fifo full or output buffer empty */ while (queue_count(uart_out) && QUEUE_REMOVE_UNITS(uart_out, &c, 1)) uartn_write_char(config->uart, c); /* If output buffer is empty, disable transmit interrupt */ if (!queue_count(uart_out)) uartn_tx_stop(config->uart); } void send_data_to_usb(struct usart_config const *config) { struct queue const *uart_in = config->producer.queue; int uart = config->uart; size_t count; size_t q_room; size_t tail; size_t mask; q_room = queue_space(uart_in); mask = uart_in->buffer_units_mask; tail = uart_in->state->tail & mask; count = 0; #ifdef BOARD_CR50 if (ec_comm_is_uart_in_packet_mode(uart)) { /* * Even if UART-to-USB data queue is full (count == q_room), * It should drain UART queue, so that an EC packet * can be processed. In this case, EC console data * shall be lost anyway. */ while (uartn_rx_available(uart)) { uint8_t ch = uartn_read_char(uart); if (ec_comm_process_packet(ch)) continue; if ((count != q_room) && uart_ec_bridge_is_enabled()) { uart_in->buffer[tail] = ch; tail = (tail + 1) & mask; count++; } } if (count) queue_advance_tail(uart_in, count); return; } /* * If UART-to-USB bridging is not allowed, do not put any output * data to uart_in queue. */ if ((uart == UART_EC) && !uart_ec_bridge_is_enabled()) return; #endif /* BOARD_CR50 */ while ((count != q_room) && uartn_rx_available(uart)) { uart_in->buffer[tail] = uartn_read_char(uart); tail = (tail + 1) & mask; count++; } if (count) queue_advance_tail(uart_in, count); } static void uart_read(struct producer const *producer, size_t count) { } static void uart_written(struct consumer const *consumer, size_t count) { struct usart_config const *config = DOWNCAST(consumer, struct usart_config, consumer); #ifdef CONFIG_UART_BITBANG if (uart_bitbang_is_enabled() && (config->uart == bitbang_config.uart)) { uart_bitbang_drain_tx_queue(consumer->queue); return; } #endif #ifdef BOARD_CR50 if (config->uart == UART_EC) { /* * If USB-to-UART bridging is disabled, do not forward data. * Otherwise, data could be pushed into UART TX FIFO, and * transferred to EC eventually once EC-CR50 communication * enables EC UART. */ if (!uart_ec_bridge_tx_is_enabled()) { /* * Empty the RX queue, so that host won't suffer from * congestion. Also, if data remains in the queue, then * they might be transferred when UART TX gets enabled * in future. */ queue_advance_head(consumer->queue, queue_count(consumer->queue)); return; } /* * If EC-CR50 communication is on-going, then let's not forward * console input to EC for now. */ if (ec_comm_is_uart_in_packet_mode(UART_EC)) return; } #endif /* BOARD_CR50 */ if (uartn_tx_ready(config->uart) && queue_count(consumer->queue)) uartn_tx_start(config->uart); } struct producer_ops const uart_producer_ops = { .read = uart_read, }; struct consumer_ops const uart_consumer_ops = { .written = uart_written, }; #if USE_UART_INTERRUPTS #ifdef CONFIG_STREAM_USART1 /* * Interrupt handlers for UART1 */ CONFIGURE_INTERRUPTS(ap_uart, GC_IRQNUM_UART1_RXINT, GC_IRQNUM_UART1_TXINT) #endif #ifdef CONFIG_STREAM_USART2 /* * Interrupt handlers for UART2 */ CONFIGURE_INTERRUPTS(ec_uart, GC_IRQNUM_UART2_RXINT, GC_IRQNUM_UART2_TXINT) #endif #endif