use anyhow::{bail, ensure, Result};
use rusb::{Direction, Recipient, RequestType};
use std::cell::Cell;
use std::mem::size_of;
use std::rc::Rc;
use std::time::Duration;
use zerocopy::{AsBytes, FromBytes, FromZeroes};
use crate::io::eeprom;
use crate::io::gpio::GpioPin;
use crate::io::spi::{
AssertChipSelect, MaxSizes, SpiError, Target, TargetChipDeassert, Transfer, TransferMode,
};
use crate::transport::hyperdebug::{BulkInterface, Inner};
use crate::transport::TransportError;
pub struct HyperdebugSpiTarget {
inner: Rc<Inner>,
interface: BulkInterface,
target_enable_cmd: u8,
target_idx: u8,
feature_bitmap: u16,
supports_tpm_poll: bool,
max_sizes: MaxSizes,
cs_asserted_count: Cell<u32>,
}
const TRANSFER_START_TIMEOUT: Duration = Duration::from_millis(1100);
const USB_SPI_PKT_ID_CMD_GET_USB_SPI_CONFIG: u16 = 0;
const USB_SPI_PKT_ID_RSP_USB_SPI_CONFIG: u16 = 1;
const USB_SPI_PKT_ID_CMD_TRANSFER_START: u16 = 2;
const USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE: u16 = 3;
const USB_SPI_PKT_ID_RSP_TRANSFER_START: u16 = 5;
const USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE: u16 = 6;
const USB_SPI_PKT_ID_CMD_CHIP_SELECT: u16 = 7;
const USB_SPI_PKT_ID_RSP_CHIP_SELECT: u16 = 8;
const USB_SPI_PKT_ID_CMD_EEPROM_TRANSFER_START: u16 = 9;
pub const USB_SPI_REQ_ENABLE: u8 = 0;
pub const USB_SPI_REQ_ENABLE_AP: u8 = 2;
pub const USB_SPI_REQ_ENABLE_EC: u8 = 3;
const USB_MAX_SIZE: usize = 64;
const FULL_DUPLEX: usize = 65535;
const FEATURE_BIT_FULL_DUPLEX: u16 = 0x0001;
const FEATURE_BIT_EEPROM: u16 = 0x0002;
const FEATURE_BIT_EEPROM_DUAL: u16 = 0x0004;
const FEATURE_BIT_EEPROM_QUAD: u16 = 0x0008;
const FEATURE_BIT_EEPROM_OCTO: u16 = 0x0010;
const FEATURE_BIT_EEPROM_DTR: u16 = 0x0020;
const FEATURE_BIT_EEPROM_DOUBLE_BUFFER: u16 = 0x0040;
const EEPROM_FLAGS_OPCODE_LEN_POS: u8 = 0;
const EEPROM_FLAGS_ADDR_LEN_POS: u8 = 2;
const EEPROM_FLAGS_MODE_111: u32 = 0x00000000;
const EEPROM_FLAGS_MODE_11N: u32 = 0x00000020;
const EEPROM_FLAGS_MODE_1NN: u32 = 0x00000040;
const EEPROM_FLAGS_MODE_NNN: u32 = 0x00000060;
const EEPROM_FLAGS_WIDTH_1WIRE: u32 = 0x00000000;
const EEPROM_FLAGS_WIDTH_2WIRE: u32 = 0x00000080;
const EEPROM_FLAGS_WIDTH_4WIRE: u32 = 0x00000100;
const EEPROM_FLAGS_WIDTH_8WIRE: u32 = 0x00000180;
const EEPROM_FLAGS_DTR: u32 = 0x00000200;
const EEPROM_FLAGS_DUMMY_CYCLES_POS: u8 = 10;
const EEPROM_FLAGS_GSC_READY: u32 = 0x04000000;
const EEPROM_FLAGS_TPM: u32 = 0x08000000;
const EEPROM_FLAGS_WRITE_ENABLE: u32 = 0x10000000;
const EEPROM_FLAGS_POLL_BUSY: u32 = 0x20000000;
const EEPROM_FLAGS_DOUBLE_BUFFER: u32 = 0x40000000;
const EEPROM_FLAGS_WRITE: u32 = 0x80000000;
const STATUS_SUCCESS: u16 = 0x0000;
const STATUS_TIMEOUT: u16 = 0x0001;
const STATUS_BUSY: u16 = 0x0002;
const STATUS_WRITE_COUNT_INVALID: u16 = 0x0003;
const STATUS_READ_COUNT_INVALID: u16 = 0x0004;
const STATUS_DISABLED: u16 = 0x0005;
const STATUS_RX_BAD_DATA_INDEX: u16 = 0x0006;
const STATUS_RX_DATA_OVERFLOW: u16 = 0x0007;
const STATUS_RX_UNEXPECTED_PACKET: u16 = 0x0008;
const STATUS_UNSUPPORTED_FULL_DUPLEX: u16 = 0x0009;
const STATUS_UNSUPPORTED_FLASH_MODE: u16 = 0x000A;
const STATUS_STREAMING_FIRST_SUCCESS: u16 = 0x000B;
fn status_code_description(status_code: u16) -> String {
match status_code {
STATUS_SUCCESS => "success",
STATUS_TIMEOUT => "timeout",
STATUS_BUSY => "busy",
STATUS_WRITE_COUNT_INVALID => "protocol corruption (WRITE_COUNT_INVALID)",
STATUS_READ_COUNT_INVALID => "protocol corruption (READ_COUNT_INVALID)",
STATUS_DISABLED => "port not enabled",
STATUS_RX_BAD_DATA_INDEX => "protocol corruption (RX_BAD_DATA_INDEX)",
STATUS_RX_DATA_OVERFLOW => "protocol corruption (RX_DATA_OVERFLOW)",
STATUS_RX_UNEXPECTED_PACKET => "protocol corruption (RX_UNEXPECTED_PACKET)",
STATUS_UNSUPPORTED_FULL_DUPLEX => "full duplex not supported",
STATUS_UNSUPPORTED_FLASH_MODE => "requested flash mode not supported",
_ => {
return format!("unknown error {:04x}", status_code);
}
}
.to_string()
}
#[derive(AsBytes, FromBytes, FromZeroes, Debug, Default)]
#[repr(C)]
struct RspUsbSpiConfig {
packet_id: u16,
max_write_chunk: u16,
max_read_chunk: u16,
feature_bitmap: u16,
}
#[derive(AsBytes, FromBytes, FromZeroes, Debug)]
#[repr(C)]
struct CmdTransferStart {
packet_id: u16,
write_count: u16,
read_count: u16,
data: [u8; USB_MAX_SIZE - 6],
}
impl CmdTransferStart {
fn new() -> Self {
Self {
packet_id: USB_SPI_PKT_ID_CMD_TRANSFER_START,
write_count: 0,
read_count: 0,
data: [0; USB_MAX_SIZE - 6],
}
}
}
#[derive(AsBytes, FromBytes, FromZeroes, Debug)]
#[repr(C)]
struct CmdEepromTransferStart {
packet_id: u16,
count: u16,
flags: u32,
data: [u8; USB_MAX_SIZE - 8],
}
impl CmdEepromTransferStart {
fn new() -> Self {
Self {
packet_id: USB_SPI_PKT_ID_CMD_EEPROM_TRANSFER_START,
count: 0,
flags: 0,
data: [0; USB_MAX_SIZE - 8],
}
}
}
#[derive(AsBytes, FromBytes, FromZeroes, Debug)]
#[repr(C)]
struct CmdTransferContinue {
packet_id: u16,
data_index: u16,
data: [u8; USB_MAX_SIZE - 4],
}
impl CmdTransferContinue {
fn new() -> Self {
Self {
packet_id: USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE,
data_index: 0,
data: [0; USB_MAX_SIZE - 4],
}
}
}
#[derive(AsBytes, FromBytes, FromZeroes, Debug)]
#[repr(C)]
struct RspTransferStart {
packet_id: u16,
status_code: u16,
data: [u8; USB_MAX_SIZE - 4],
}
impl RspTransferStart {
fn new() -> Self {
Self {
packet_id: 0,
status_code: 0,
data: [0; USB_MAX_SIZE - 4],
}
}
}
#[derive(AsBytes, FromBytes, FromZeroes, Debug)]
#[repr(C)]
struct RspTransferContinue {
packet_id: u16,
data_index: u16,
data: [u8; USB_MAX_SIZE - 4],
}
impl RspTransferContinue {
fn new() -> Self {
Self {
packet_id: 0,
data_index: 0,
data: [0; USB_MAX_SIZE - 4],
}
}
}
#[derive(AsBytes, FromBytes, FromZeroes, Debug)]
#[repr(C)]
struct CmdChipSelect {
packet_id: u16,
flags: u16,
}
impl CmdChipSelect {
fn new(assert_chip_select: bool) -> Self {
Self {
packet_id: USB_SPI_PKT_ID_CMD_CHIP_SELECT,
flags: u16::from(assert_chip_select),
}
}
}
#[derive(AsBytes, FromBytes, FromZeroes, Debug, Default)]
#[repr(C)]
struct RspChipSelect {
packet_id: u16,
status_code: u16,
}
impl RspChipSelect {
fn new() -> Self {
Self {
packet_id: 0,
status_code: 0,
}
}
}
enum StreamState<'a> {
NoPending,
PendingWrite,
PendingRead(&'a mut [u8]),
}
impl HyperdebugSpiTarget {
pub fn open(
inner: &Rc<Inner>,
spi_interface: &BulkInterface,
enable_cmd: u8,
idx: u8,
supports_tpm_poll: bool,
) -> Result<Self> {
let mut usb_handle = inner.usb_device.borrow_mut();
inner.selected_spi.set(idx);
usb_handle.write_control(
rusb::request_type(Direction::Out, RequestType::Vendor, Recipient::Interface),
enable_cmd,
idx as u16,
spi_interface.interface as u16,
&[],
)?;
usb_handle.claim_interface(spi_interface.interface)?;
usb_handle.write_bulk(
spi_interface.out_endpoint,
&USB_SPI_PKT_ID_CMD_GET_USB_SPI_CONFIG.to_le_bytes(),
)?;
let mut resp: RspUsbSpiConfig = Default::default();
let rc = usb_handle.read_bulk(spi_interface.in_endpoint, resp.as_bytes_mut())?;
ensure!(
rc == size_of::<RspUsbSpiConfig>(),
TransportError::CommunicationError(
"Unrecognized reponse to GET_USB_SPI_CONFIG".to_string()
)
);
ensure!(
resp.packet_id == USB_SPI_PKT_ID_RSP_USB_SPI_CONFIG,
TransportError::CommunicationError(
"Unrecognized reponse to GET_USB_SPI_CONFIG".to_string()
)
);
Ok(Self {
inner: Rc::clone(inner),
interface: *spi_interface,
target_enable_cmd: enable_cmd,
target_idx: idx,
feature_bitmap: resp.feature_bitmap,
supports_tpm_poll,
max_sizes: MaxSizes {
read: resp.max_read_chunk as usize,
write: resp.max_write_chunk as usize,
},
cs_asserted_count: Cell::new(0),
})
}
fn select_my_spi_bus(&self) -> Result<()> {
if self.inner.selected_spi.get() != self.target_idx {
self.inner.selected_spi.set(self.target_idx);
self.inner.usb_device.borrow().write_control(
rusb::request_type(Direction::Out, RequestType::Vendor, Recipient::Interface),
self.target_enable_cmd,
self.target_idx as u16,
self.interface.interface as u16,
&[],
)?;
}
Ok(())
}
fn transmit(&self, wbuf: &[u8], rbuf_len: usize) -> Result<()> {
let mut req = CmdTransferStart::new();
req.write_count = wbuf.len() as u16;
req.read_count = rbuf_len as u16;
let databytes = std::cmp::min(USB_MAX_SIZE - 6, wbuf.len());
req.data[0..databytes].clone_from_slice(&wbuf[0..databytes]);
self.usb_write_bulk(&req.as_bytes()[0..6 + databytes])?;
let mut index = databytes;
while index < wbuf.len() {
let mut req = CmdTransferContinue::new();
req.data_index = index as u16;
let databytes = std::cmp::min(USB_MAX_SIZE - 4, wbuf.len() - index);
req.data[0..databytes].clone_from_slice(&wbuf[index..index + databytes]);
self.usb_write_bulk(&req.as_bytes()[0..4 + databytes])?;
index += databytes;
}
Ok(())
}
fn tpm_transmit(&self, wbuf: &[u8], rbuf_len: usize, await_gsc_ready: bool) -> Result<()> {
const TPM_HEADER_SIZE: usize = 4;
let mut req = CmdEepromTransferStart::new();
if rbuf_len == 0 {
req.flags |= EEPROM_FLAGS_WRITE;
req.count = (wbuf.len() - TPM_HEADER_SIZE) as u16;
ensure!(
wbuf.len() > TPM_HEADER_SIZE,
SpiError::InvalidDataLength(wbuf.len())
);
} else {
req.count = rbuf_len as u16;
ensure!(
wbuf.len() == TPM_HEADER_SIZE,
SpiError::InvalidDataLength(wbuf.len())
);
}
req.flags |= (TPM_HEADER_SIZE as u32) << EEPROM_FLAGS_ADDR_LEN_POS;
req.flags |= EEPROM_FLAGS_TPM;
if await_gsc_ready {
req.flags |= EEPROM_FLAGS_GSC_READY;
}
let data_start_offset = 0;
let databytes = std::cmp::min(USB_MAX_SIZE - 8 - data_start_offset, wbuf.len());
req.data[data_start_offset..data_start_offset + databytes]
.clone_from_slice(&wbuf[0..databytes]);
self.usb_write_bulk(&req.as_bytes()[0..8 + data_start_offset + databytes])?;
let mut index = databytes;
while index < wbuf.len() {
let mut req = CmdTransferContinue::new();
req.data_index = index as u16;
let databytes = std::cmp::min(USB_MAX_SIZE - 4, wbuf.len() - index);
req.data[0..databytes].clone_from_slice(&wbuf[index..index + databytes]);
self.usb_write_bulk(&req.as_bytes()[0..4 + databytes])?;
index += databytes;
}
Ok(())
}
fn receive(&self, rbuf: &mut [u8]) -> Result<()> {
let mut resp = RspTransferStart::new();
let bytecount = self.usb_read_bulk_timeout(resp.as_bytes_mut(), TRANSFER_START_TIMEOUT)?;
ensure!(
bytecount >= 4,
TransportError::CommunicationError("Short reponse to TRANSFER_START".to_string())
);
ensure!(
resp.packet_id == USB_SPI_PKT_ID_RSP_TRANSFER_START,
TransportError::CommunicationError(format!(
"Unrecognized reponse to TRANSFER_START: {}",
resp.packet_id
))
);
ensure!(
resp.status_code == STATUS_SUCCESS,
TransportError::CommunicationError(format!(
"SPI error: {}",
status_code_description(resp.status_code)
))
);
let databytes = bytecount - 4;
rbuf[0..databytes].clone_from_slice(&resp.data[0..databytes]);
let mut index = databytes;
while index < rbuf.len() {
let mut resp = RspTransferContinue::new();
let bytecount = self.usb_read_bulk(resp.as_bytes_mut())?;
ensure!(
bytecount > 4,
TransportError::CommunicationError(
"Short reponse to TRANSFER_CONTINUE".to_string()
)
);
ensure!(
resp.packet_id == USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE,
TransportError::CommunicationError(format!(
"Unrecognized reponse to TRANSFER_CONTINUE: {}",
resp.packet_id
))
);
ensure!(
resp.data_index == index as u16,
TransportError::CommunicationError(
"Unexpected byte index in reponse to TRANSFER_START".to_string()
)
);
let databytes = bytecount - 4;
rbuf[index..index + databytes].clone_from_slice(&resp.data[0..databytes]);
index += databytes;
}
Ok(())
}
fn receive_first_streaming(&self) -> Result<()> {
let mut resp = RspTransferStart::new();
let bytecount = self.usb_read_bulk(resp.as_bytes_mut())?;
ensure!(
bytecount >= 4,
TransportError::CommunicationError("Short reponse to TRANSFER_START".to_string())
);
ensure!(
resp.packet_id == USB_SPI_PKT_ID_RSP_TRANSFER_START,
TransportError::CommunicationError(format!(
"Unrecognized reponse to TRANSFER_START: {}",
resp.packet_id
))
);
ensure!(
resp.status_code == STATUS_STREAMING_FIRST_SUCCESS,
TransportError::CommunicationError(format!(
"SPI error: {}, expected streaming response",
status_code_description(resp.status_code)
))
);
Ok(())
}
fn verify_width(&self, requested_width: eeprom::DataWidth) -> Result<()> {
match requested_width {
eeprom::DataWidth::Single => (),
eeprom::DataWidth::Dual => ensure!(
self.feature_bitmap & FEATURE_BIT_EEPROM_DUAL != 0,
SpiError::InvalidDataWidth(requested_width)
),
eeprom::DataWidth::Quad => ensure!(
self.feature_bitmap & FEATURE_BIT_EEPROM_QUAD != 0,
SpiError::InvalidDataWidth(requested_width)
),
eeprom::DataWidth::Octo => ensure!(
self.feature_bitmap & FEATURE_BIT_EEPROM_OCTO != 0,
SpiError::InvalidDataWidth(requested_width)
),
}
Ok(())
}
fn eeprom_transmit<'a>(
&self,
write_enable: Option<&eeprom::Cmd>,
cmd: &eeprom::Cmd,
wbuf: &[u8],
rbuf: &'a mut [u8],
wait_for_busy_clear: bool,
stream_state: StreamState,
) -> Result<StreamState<'a>> {
let double_buffer = self.feature_bitmap & FEATURE_BIT_EEPROM_DOUBLE_BUFFER != 0;
self.verify_width(cmd.get_width())?;
let mut req = CmdEepromTransferStart::new();
let mut idx = 0;
if rbuf.is_empty() {
req.flags |= EEPROM_FLAGS_WRITE;
req.count = wbuf.len() as u16;
} else {
req.count = rbuf.len() as u16;
}
req.flags |= match cmd.get_switch() {
eeprom::Switch::Mode111 => EEPROM_FLAGS_MODE_111,
eeprom::Switch::Mode11N => EEPROM_FLAGS_MODE_11N,
eeprom::Switch::Mode1NN => EEPROM_FLAGS_MODE_1NN,
eeprom::Switch::ModeNNN => EEPROM_FLAGS_MODE_NNN,
};
req.flags |= match cmd.get_width() {
eeprom::DataWidth::Single => EEPROM_FLAGS_WIDTH_1WIRE,
eeprom::DataWidth::Dual => EEPROM_FLAGS_WIDTH_2WIRE,
eeprom::DataWidth::Quad => EEPROM_FLAGS_WIDTH_4WIRE,
eeprom::DataWidth::Octo => EEPROM_FLAGS_WIDTH_8WIRE,
};
if cmd.get_double_transfer_rate() {
ensure!(
self.feature_bitmap & FEATURE_BIT_EEPROM_DTR != 0,
SpiError::InvalidDoubleTransferRate()
);
req.flags |= EEPROM_FLAGS_DTR;
}
req.flags |= (cmd.get_opcode_len() as u32) << EEPROM_FLAGS_OPCODE_LEN_POS;
if double_buffer {
req.flags |= EEPROM_FLAGS_DOUBLE_BUFFER;
}
if let Some(pre_cmd) = write_enable {
req.flags |= EEPROM_FLAGS_WRITE_ENABLE;
let opcode_bytes = pre_cmd.get_opcode();
if opcode_bytes.len() != 1 {
panic!("Illegal write enable sequence");
}
req.data[idx..idx + opcode_bytes.len()].clone_from_slice(opcode_bytes);
idx += opcode_bytes.len();
}
if wait_for_busy_clear {
req.flags |= EEPROM_FLAGS_POLL_BUSY;
}
let opcode_bytes = cmd.get_opcode();
let address_bytes =
&cmd.get_address().to_be_bytes()[(4 - cmd.get_address_len()) as usize..];
req.data[idx..idx + opcode_bytes.len()].clone_from_slice(opcode_bytes);
idx += opcode_bytes.len();
let mut addr_len = cmd.get_address_len();
req.data[idx..idx + address_bytes.len()].clone_from_slice(address_bytes);
idx += address_bytes.len();
if cmd.get_switch() == eeprom::Switch::Mode111
&& cmd.get_dummy_cycles() % 8 == 0
&& addr_len + cmd.get_dummy_cycles() / 8 <= 7
{
addr_len += cmd.get_dummy_cycles() / 8;
for _ in 0..cmd.get_dummy_cycles() / 8 {
req.data[idx] = 0x00;
idx += 1;
}
} else if cmd.get_dummy_cycles() < 32 {
req.flags |= (cmd.get_dummy_cycles() as u32) << EEPROM_FLAGS_DUMMY_CYCLES_POS
} else {
bail!(SpiError::InvalidDummyCycles(cmd.get_dummy_cycles()));
}
req.flags |= (addr_len as u32) << EEPROM_FLAGS_ADDR_LEN_POS;
let data_start_offset = idx;
let databytes = std::cmp::min(USB_MAX_SIZE - 8 - data_start_offset, wbuf.len());
req.data[data_start_offset..data_start_offset + databytes]
.clone_from_slice(&wbuf[0..databytes]);
self.usb_write_bulk(&req.as_bytes()[0..8 + data_start_offset + databytes])?;
let mut index = databytes;
while index < wbuf.len() {
let mut req = CmdTransferContinue::new();
req.data_index = index as u16;
let databytes = std::cmp::min(USB_MAX_SIZE - 4, wbuf.len() - index);
req.data[0..databytes].clone_from_slice(&wbuf[index..index + databytes]);
self.usb_write_bulk(&req.as_bytes()[0..4 + databytes])?;
index += databytes;
}
let next_stream_state = if rbuf.is_empty() {
StreamState::PendingWrite
} else {
StreamState::PendingRead(rbuf)
};
if double_buffer {
self.receive_streamed_data(stream_state)?;
Ok(next_stream_state)
} else {
self.receive_streamed_data(next_stream_state)?;
Ok(StreamState::NoPending)
}
}
fn receive_streamed_data(&self, stream_state: StreamState) -> Result<()> {
match stream_state {
StreamState::NoPending => self.receive_first_streaming(),
StreamState::PendingWrite => self.receive(&mut []),
StreamState::PendingRead(rbuf) => self.receive(rbuf),
}
}
fn eeprom_transmit_get_last_streamed_data(&self) -> Result<()> {
let mut req = CmdEepromTransferStart::new();
req.count = 0;
req.flags = EEPROM_FLAGS_DOUBLE_BUFFER;
self.usb_write_bulk(&req.as_bytes()[0..8])
}
fn get_last_streamed_data(&self, stream_state: StreamState) -> Result<()> {
match stream_state {
StreamState::NoPending => Ok(()),
StreamState::PendingWrite => {
self.eeprom_transmit_get_last_streamed_data()?;
self.receive(&mut [])
}
StreamState::PendingRead(rbuf) => {
self.eeprom_transmit_get_last_streamed_data()?;
self.receive(rbuf)
}
}
}
fn do_assert_cs(&self, assert: bool) -> Result<()> {
let mut count = self.cs_asserted_count.get();
if assert {
if count == 0 {
self._do_assert_cs(assert)?;
}
count += 1;
} else {
if count == 1 {
self._do_assert_cs(assert)?;
}
count -= 1;
}
self.cs_asserted_count.set(count);
Ok(())
}
fn _do_assert_cs(&self, assert: bool) -> Result<()> {
let req = CmdChipSelect::new(assert);
self.usb_write_bulk(req.as_bytes())?;
let mut resp = RspChipSelect::new();
let bytecount = self.usb_read_bulk(resp.as_bytes_mut())?;
ensure!(
bytecount >= 4,
TransportError::CommunicationError("Unrecognized reponse to CHIP_SELECT".to_string())
);
ensure!(
resp.packet_id == USB_SPI_PKT_ID_RSP_CHIP_SELECT,
TransportError::CommunicationError("Unrecognized reponse to CHIP_SELECT".to_string())
);
ensure!(
resp.status_code == STATUS_SUCCESS,
TransportError::CommunicationError(format!("SPI error: {}", resp.status_code))
);
Ok(())
}
fn usb_write_bulk(&self, buf: &[u8]) -> Result<()> {
self.inner
.usb_device
.borrow()
.write_bulk(self.interface.out_endpoint, buf)?;
Ok(())
}
fn usb_read_bulk(&self, buf: &mut [u8]) -> Result<usize> {
self.inner
.usb_device
.borrow()
.read_bulk(self.interface.in_endpoint, buf)
}
fn usb_read_bulk_timeout(&self, buf: &mut [u8], timeout: Duration) -> Result<usize> {
self.inner
.usb_device
.borrow()
.read_bulk_timeout(self.interface.in_endpoint, buf, timeout)
}
}
impl Target for HyperdebugSpiTarget {
fn get_transfer_mode(&self) -> Result<TransferMode> {
Ok(TransferMode::Mode0)
}
fn set_transfer_mode(&self, _mode: TransferMode) -> Result<()> {
todo!();
}
fn get_bits_per_word(&self) -> Result<u32> {
Ok(8)
}
fn set_bits_per_word(&self, bits_per_word: u32) -> Result<()> {
match bits_per_word {
8 => Ok(()),
_ => Err(SpiError::InvalidWordSize(bits_per_word).into()),
}
}
fn get_max_speed(&self) -> Result<u32> {
let mut buf = String::new();
let captures = self.inner.cmd_one_line_output_match(
&format!("spi info {}", &self.target_idx),
&super::SPI_REGEX,
&mut buf,
)?;
Ok(captures.get(3).unwrap().as_str().parse().unwrap())
}
fn set_max_speed(&self, frequency: u32) -> Result<()> {
self.inner
.cmd_no_output(&format!("spi set speed {} {}", &self.target_idx, frequency))
}
fn supports_bidirectional_transfer(&self) -> Result<bool> {
Ok((self.feature_bitmap & FEATURE_BIT_FULL_DUPLEX) != 0)
}
fn supports_tpm_poll(&self) -> Result<bool> {
Ok(self.supports_tpm_poll)
}
fn set_pins(
&self,
serial_clock: Option<&Rc<dyn GpioPin>>,
host_out_device_in: Option<&Rc<dyn GpioPin>>,
host_in_device_out: Option<&Rc<dyn GpioPin>>,
chip_select: Option<&Rc<dyn GpioPin>>,
gsc_ready: Option<&Rc<dyn GpioPin>>,
) -> Result<()> {
if serial_clock.is_some() || host_out_device_in.is_some() || host_in_device_out.is_some() {
bail!(SpiError::InvalidPin);
}
if let Some(pin) = chip_select {
self.inner.cmd_no_output(&format!(
"spi set cs {} {}",
&self.target_idx,
pin.get_internal_pin_name().ok_or(SpiError::InvalidPin)?
))?;
}
if let Some(pin) = gsc_ready {
self.inner.cmd_no_output(&format!(
"spi set ready {} {}",
&self.target_idx,
pin.get_internal_pin_name().ok_or(SpiError::InvalidPin)?
))?;
}
Ok(())
}
fn get_max_transfer_count(&self) -> Result<usize> {
Ok(usize::MAX)
}
fn get_max_transfer_sizes(&self) -> Result<MaxSizes> {
Ok(self.max_sizes)
}
fn get_flashrom_programmer(&self) -> Result<String> {
Ok(format!(
"raiden_debug_spi:serial={},target={}",
self.inner.usb_device.borrow().get_serial_number(),
self.target_idx
))
}
fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()> {
let mut idx: usize = 0;
self.select_my_spi_bus()?;
match transaction {
[Transfer::Write(wbuf), Transfer::Read(rbuf)] => {
ensure!(
wbuf.len() <= self.max_sizes.write,
SpiError::InvalidDataLength(wbuf.len())
);
ensure!(
rbuf.len() <= self.max_sizes.read,
SpiError::InvalidDataLength(rbuf.len())
);
self.transmit(wbuf, rbuf.len())?;
self.receive(rbuf)?;
return Ok(());
}
[Transfer::Write(wbuf), Transfer::GscReady, Transfer::TpmPoll, Transfer::Read(rbuf)] => {
ensure!(
wbuf.len() <= self.max_sizes.write,
SpiError::InvalidDataLength(wbuf.len())
);
ensure!(
rbuf.len() <= self.max_sizes.read,
SpiError::InvalidDataLength(rbuf.len())
);
self.tpm_transmit(wbuf, rbuf.len(), true)?;
self.receive(rbuf)?;
return Ok(());
}
[Transfer::Write(wbuf), Transfer::TpmPoll, Transfer::Read(rbuf)] => {
ensure!(
wbuf.len() <= self.max_sizes.write,
SpiError::InvalidDataLength(wbuf.len())
);
ensure!(
rbuf.len() <= self.max_sizes.read,
SpiError::InvalidDataLength(rbuf.len())
);
self.tpm_transmit(wbuf, rbuf.len(), false)?;
self.receive(rbuf)?;
return Ok(());
}
[Transfer::Write(wbuf)] => {
ensure!(
wbuf.len() <= self.max_sizes.write,
SpiError::InvalidDataLength(wbuf.len())
);
self.transmit(wbuf, 0)?;
self.receive(&mut [])?;
return Ok(());
}
[Transfer::Write(wbuf1), Transfer::Write(wbuf2)] => {
if wbuf1.len() + wbuf2.len() <= self.max_sizes.write {
let mut combined_buf = vec![0u8; wbuf1.len() + wbuf2.len()];
combined_buf[..wbuf1.len()].clone_from_slice(wbuf1);
combined_buf[wbuf1.len()..].clone_from_slice(wbuf2);
self.transmit(&combined_buf, 0)?;
self.receive(&mut [])?;
return Ok(());
}
}
[Transfer::Write(wbuf1), Transfer::TpmPoll, Transfer::Write(wbuf2), Transfer::GscReady] =>
{
ensure!(
wbuf1.len() + wbuf2.len() <= self.max_sizes.write,
SpiError::InvalidDataLength(wbuf1.len() + wbuf2.len())
);
let mut combined_buf = vec![0u8; wbuf1.len() + wbuf2.len()];
combined_buf[..wbuf1.len()].clone_from_slice(wbuf1);
combined_buf[wbuf1.len()..].clone_from_slice(wbuf2);
self.tpm_transmit(&combined_buf, 0, true)?;
self.receive(&mut [])?;
return Ok(());
}
[Transfer::Write(wbuf1), Transfer::TpmPoll, Transfer::Write(wbuf2)] => {
ensure!(
wbuf1.len() + wbuf2.len() <= self.max_sizes.write,
SpiError::InvalidDataLength(wbuf1.len() + wbuf2.len())
);
let mut combined_buf = vec![0u8; wbuf1.len() + wbuf2.len()];
combined_buf[..wbuf1.len()].clone_from_slice(wbuf1);
combined_buf[wbuf1.len()..].clone_from_slice(wbuf2);
self.tpm_transmit(&combined_buf, 0, false)?;
self.receive(&mut [])?;
return Ok(());
}
[Transfer::Read(rbuf)] => {
ensure!(
rbuf.len() <= self.max_sizes.read,
SpiError::InvalidDataLength(rbuf.len())
);
self.transmit(&[], rbuf.len())?;
self.receive(rbuf)?;
return Ok(());
}
_ => (),
}
self.do_assert_cs(true)?;
while idx < transaction.len() {
match &mut transaction[idx..] {
[Transfer::Write(wbuf), Transfer::Read(rbuf), ..] => {
ensure!(
wbuf.len() <= self.max_sizes.write,
SpiError::InvalidDataLength(wbuf.len())
);
ensure!(
rbuf.len() <= self.max_sizes.read,
SpiError::InvalidDataLength(rbuf.len())
);
self.transmit(wbuf, rbuf.len())?;
self.receive(rbuf)?;
idx += 2;
continue;
}
[Transfer::Write(wbuf), ..] => {
ensure!(
wbuf.len() <= self.max_sizes.write,
SpiError::InvalidDataLength(wbuf.len())
);
self.transmit(wbuf, 0)?;
self.receive(&mut [])?;
}
[Transfer::Read(rbuf), ..] => {
ensure!(
rbuf.len() <= self.max_sizes.read,
SpiError::InvalidDataLength(rbuf.len())
);
self.transmit(&[], rbuf.len())?;
self.receive(rbuf)?;
}
[Transfer::Both(wbuf, rbuf), ..] => {
ensure!(
(self.feature_bitmap & FEATURE_BIT_FULL_DUPLEX) != 0,
TransportError::CommunicationError(
"HyperDebug does not support bidirectional SPI".to_string()
)
);
ensure!(
rbuf.len() == wbuf.len(),
SpiError::MismatchedDataLength(wbuf.len(), rbuf.len())
);
ensure!(
wbuf.len() <= self.max_sizes.read && wbuf.len() <= self.max_sizes.write,
SpiError::InvalidDataLength(wbuf.len())
);
self.transmit(wbuf, FULL_DUPLEX)?;
self.receive(rbuf)?;
}
[Transfer::TpmPoll, ..] => bail!(TransportError::UnsupportedOperation),
[Transfer::GscReady, ..] => bail!(TransportError::UnsupportedOperation),
[] => (),
}
idx += 1;
}
self.do_assert_cs(false)?;
Ok(())
}
fn run_eeprom_transactions(&self, mut transactions: &mut [eeprom::Transaction]) -> Result<()> {
if self.feature_bitmap & FEATURE_BIT_EEPROM == 0 {
return eeprom::default_run_eeprom_transactions(self, transactions);
}
self.select_my_spi_bus()?;
let mut stream_state = StreamState::NoPending;
loop {
match transactions {
[eeprom::Transaction::Command(pre_cmd), eeprom::Transaction::Write(cmd, wbuf), eeprom::Transaction::WaitForBusyClear, rest @ ..] =>
{
if pre_cmd.get_opcode().len() == 1 {
stream_state = self.eeprom_transmit(
Some(pre_cmd), cmd,
wbuf,
&mut [],
true, stream_state,
)?;
} else {
stream_state =
self.eeprom_transmit(None, pre_cmd, &[], &mut [], false, stream_state)?;
stream_state = self.eeprom_transmit(
None, cmd,
wbuf,
&mut [],
true, stream_state,
)?;
}
transactions = rest;
}
[eeprom::Transaction::Command(cmd), rest @ ..] => {
stream_state =
self.eeprom_transmit(None, cmd, &[], &mut [], false, stream_state)?;
transactions = rest;
}
[eeprom::Transaction::Read(cmd, ref mut rbuf), rest @ ..] => {
stream_state =
self.eeprom_transmit(None, cmd, &[], rbuf, false, stream_state)?;
transactions = rest;
}
[eeprom::Transaction::Write(cmd, wbuf), eeprom::Transaction::WaitForBusyClear, rest @ ..] =>
{
stream_state = self.eeprom_transmit(
None, cmd,
wbuf,
&mut [],
true, stream_state,
)?;
transactions = rest;
}
[eeprom::Transaction::Write(cmd, wbuf), rest @ ..] => {
stream_state =
self.eeprom_transmit(None, cmd, wbuf, &mut [], false, stream_state)?;
transactions = rest;
}
[eeprom::Transaction::WaitForBusyClear, rest @ ..] => {
self.get_last_streamed_data(stream_state)?;
let mut status = eeprom::STATUS_WIP;
while status & eeprom::STATUS_WIP != 0 {
self.run_transaction(&mut [
Transfer::Write(&[eeprom::READ_STATUS]),
Transfer::Read(std::slice::from_mut(&mut status)),
])?;
}
stream_state = StreamState::NoPending;
transactions = rest;
}
[] => {
return self.get_last_streamed_data(stream_state);
}
}
}
}
fn assert_cs(self: Rc<Self>) -> Result<AssertChipSelect> {
self.do_assert_cs(true)?;
Ok(AssertChipSelect::new(self))
}
}
impl TargetChipDeassert for HyperdebugSpiTarget {
fn deassert_cs(&self) {
self.do_assert_cs(false)
.expect("Error while deasserting CS");
}
}