opentitanlib/transport/hyperdebug/
uart.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::rc::Rc;
6
7use anyhow::{Context, Result};
8use rusb::{Direction, Recipient, RequestType};
9use serialport::Parity;
10
11use super::UartInterface;
12use crate::io::uart::{FlowControl, Uart, UartError};
13use crate::transport::TransportError;
14use crate::transport::common::uart::{SerialPortUart, SoftwareFlowControl};
15use crate::transport::hyperdebug::Inner;
16
17const UART_BAUD: u32 = 115200;
18
19pub struct HyperdebugUart {
20    inner: Rc<Inner>,
21    usb_interface: u8,
22    supports_clearing_queues: bool,
23    serial_port: SoftwareFlowControl<SerialPortUart>,
24}
25
26#[allow(dead_code)]
27enum ControlRequest {
28    ReqParity = 0,
29    SetParity = 1,
30    ReqBaud = 2,
31    SetBaud = 3,
32    Break = 4,
33    ClearQueues = 5,
34}
35
36/// Request clearing the queue of UART data having been received by HyperDebug.
37#[allow(dead_code)]
38const CLEAR_RX_FIFO: u16 = 0x0001;
39/// Request clearing the queue of data to be transmitted by HyperDebug UART.
40#[allow(dead_code)]
41const CLEAR_TX_FIFO: u16 = 0x0002;
42
43impl HyperdebugUart {
44    pub fn open(
45        inner: &Rc<Inner>,
46        uart_interface: &UartInterface,
47        supports_clearing_queues: bool,
48    ) -> Result<Self> {
49        Ok(Self {
50            inner: Rc::clone(inner),
51            usb_interface: uart_interface.interface,
52            supports_clearing_queues,
53            serial_port: SoftwareFlowControl::new(SerialPortUart::open(
54                uart_interface
55                    .tty
56                    .to_str()
57                    .ok_or(TransportError::UnicodePathError)?,
58                UART_BAUD,
59            )?),
60        })
61    }
62}
63
64impl Uart for HyperdebugUart {
65    fn get_baudrate(&self) -> Result<u32> {
66        let usb_handle = self.inner.usb_device.borrow();
67        let mut data = [0u8, 0u8];
68        usb_handle.read_control(
69            rusb::request_type(Direction::In, RequestType::Vendor, Recipient::Interface),
70            ControlRequest::ReqBaud as u8,
71            0,
72            self.usb_interface as u16,
73            &mut data,
74        )?;
75        Ok(u16::from_le_bytes(data) as u32 * 100)
76    }
77
78    fn set_baudrate(&self, baudrate: u32) -> Result<()> {
79        let usb_handle = self.inner.usb_device.borrow();
80        let compressed_baudrate: u16 = ((baudrate + 50) / 100).try_into()?;
81        let decompressed_baudrate = compressed_baudrate as u32 * 100;
82        if decompressed_baudrate != baudrate {
83            log::warn!(
84                "Warning: accuracy loss when setting baud rate. UART will use {} Bd instead of {}",
85                decompressed_baudrate,
86                baudrate
87            );
88        }
89        usb_handle.write_control(
90            rusb::request_type(Direction::Out, RequestType::Vendor, Recipient::Interface),
91            ControlRequest::SetBaud as u8,
92            compressed_baudrate,
93            self.usb_interface as u16,
94            &[],
95        )?;
96        Ok(())
97    }
98
99    fn get_flow_control(&self) -> Result<FlowControl> {
100        self.serial_port.get_flow_control()
101    }
102
103    fn set_flow_control(&self, flow_control: bool) -> Result<()> {
104        self.serial_port.set_flow_control(flow_control)
105    }
106
107    fn get_device_path(&self) -> Result<String> {
108        self.serial_port.get_device_path()
109    }
110
111    fn poll_read(
112        &self,
113        cx: &mut std::task::Context<'_>,
114        buf: &mut [u8],
115    ) -> std::task::Poll<Result<usize>> {
116        self.serial_port.poll_read(cx, buf)
117    }
118
119    fn write(&self, buf: &[u8]) -> Result<()> {
120        self.serial_port.write(buf)
121    }
122
123    fn clear_rx_buffer(&self) -> Result<()> {
124        if self.supports_clearing_queues {
125            let usb_handle = self.inner.usb_device.borrow();
126            usb_handle.write_control(
127                rusb::request_type(Direction::Out, RequestType::Vendor, Recipient::Interface),
128                ControlRequest::ClearQueues as u8,
129                CLEAR_RX_FIFO,
130                self.usb_interface as u16,
131                &[],
132            )?;
133        }
134        self.serial_port.clear_rx_buffer()
135    }
136
137    fn set_break(&self, enable: bool) -> Result<()> {
138        let usb_handle = self.inner.usb_device.borrow();
139        usb_handle
140            .write_control(
141                rusb::request_type(Direction::Out, RequestType::Vendor, Recipient::Interface),
142                ControlRequest::Break as u8,
143                if enable { 0xFFFF } else { 0 },
144                self.usb_interface as u16,
145                &[],
146            )
147            .context("Setting break condition")?;
148        Ok(())
149    }
150
151    fn set_parity(&self, parity: Parity) -> Result<()> {
152        // Parity values taken from https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/main/chip/stm32/usart.c#196
153        let parity_code = match parity {
154            Parity::None => 0,
155            Parity::Odd => 1,
156            Parity::Even => 2,
157        };
158
159        let usb_handle = self.inner.usb_device.borrow();
160        usb_handle.write_control(
161            rusb::request_type(Direction::Out, RequestType::Vendor, Recipient::Interface),
162            ControlRequest::SetParity as u8,
163            parity_code,
164            self.usb_interface as u16,
165            &[],
166        )?;
167        Ok(())
168    }
169
170    fn get_parity(&self) -> Result<Parity> {
171        let usb_handle = self.inner.usb_device.borrow();
172        let mut data = [0u8, 0u8];
173        usb_handle.read_control(
174            rusb::request_type(Direction::In, RequestType::Vendor, Recipient::Interface),
175            ControlRequest::ReqParity as u8,
176            0,
177            self.usb_interface as u16,
178            &mut data,
179        )?;
180        // Parity values taken from https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/main/chip/stm32/usart.c#180
181        match u16::from_le_bytes(data) {
182            0 => Ok(Parity::None),
183            1 => Ok(Parity::Odd),
184            2 => Ok(Parity::Even),
185            _ => Err(UartError::ReadError("Unknown parity value".to_string()).into()),
186        }
187    }
188}