use tracing::debug;
use crate::{
Etp, EtpOperations,
error::{EtpError, EtpResult},
protocol::{ETP_MESSAGE_HEADER_SIZE, ETP_MESSAGE_HEADER_STATUS_INDEX},
util::{self, u32_vec_to_le_bytes},
};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub enum GpioMode {
Input,
Output,
}
#[derive(Debug, Clone, PartialEq)]
pub enum GpioType {
PushPull,
PullUp,
PullDown,
OpenDrain,
}
#[derive(Debug, Clone, PartialEq)]
pub enum InterruptMode {
None,
RisingEdge,
FallingEdge,
BothEdges,
}
pub type Pin = u8;
pub type Port = Option<char>;
pub type GpioInfo = HashMap<Port, Vec<Pin>>;
#[derive(Debug, Clone)]
pub struct GpioPinConfig {
pub pin_number: Pin,
pub port: Port,
pub mode: GpioMode,
pub pin_type: Option<GpioType>,
pub interrupt_mode: Option<InterruptMode>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum GpioPinStatus {
High,
Low,
}
pub type GpioPinsStatus = HashMap<(Port, Pin), GpioPinStatus>;
#[repr(u8)]
pub(crate) enum GpioOperations {
GpioInfo = 0x00,
GpioInit = 0x01,
GpioRead = 0x02,
GpioWrite = 0x03,
}
impl TryFrom<u8> for GpioOperations {
type Error = EtpError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x00 => Ok(GpioOperations::GpioInfo),
0x01 => Ok(GpioOperations::GpioInit),
0x02 => Ok(GpioOperations::GpioRead),
0x03 => Ok(GpioOperations::GpioWrite),
_ => Err(EtpError::InvalidByteLength),
}
}
}
#[repr(u8)]
#[derive(Debug, Clone)]
pub(crate) enum GpioInfoCommands {
GpioPortCount = 0x01,
GpioPinCount = 0x02,
GpioPorts = 0x03,
GpioPins = 0x04,
}
impl TryFrom<u8> for GpioInfoCommands {
type Error = EtpError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(GpioInfoCommands::GpioPortCount),
0x02 => Ok(GpioInfoCommands::GpioPinCount),
0x03 => Ok(GpioInfoCommands::GpioPorts),
0x04 => Ok(GpioInfoCommands::GpioPins),
_ => Err(EtpError::InvalidByteLength),
}
}
}
impl GpioPinConfig {
pub fn new(
pin_number: Pin,
port: Port,
mode: GpioMode,
pin_type: Option<GpioType>,
interrupt_mode: Option<InterruptMode>,
) -> EtpResult<Self> {
Ok(GpioPinConfig {
pin_number,
port,
mode,
pin_type,
interrupt_mode,
})
}
}
pub struct Gpio {
pub pins: Vec<GpioPinConfig>,
}
impl Gpio {
pub(crate) fn new() -> Self {
Gpio { pins: Vec::new() }
}
}
impl Etp {
pub fn gpio_init(&mut self, pins: Vec<GpioPinConfig>) -> EtpResult<()> {
let mut dir_mask: u32 = 0;
let mut dir_value: u32 = 0;
let mut pull_mask: u32 = 0;
let mut pull_value: u32 = 0;
let mut interrupt_mask: u32 = 0;
let mut interrupt_value: u32 = 0;
for pin in pins.iter() {
dir_mask |= 1 << pin.pin_number;
if pin.mode == GpioMode::Input {
dir_value |= 1 << pin.pin_number;
}
pull_mask |= 1 << pin.pin_number;
if let Some(pin_type) = &pin.pin_type {
match pin_type {
GpioType::PushPull => {}
GpioType::PullUp => pull_value |= 1 << pin.pin_number,
GpioType::PullDown => pull_value |= 2 << pin.pin_number,
GpioType::OpenDrain => pull_value |= 3 << pin.pin_number,
}
} else {
}
if let Some(interrupt_mode) = &pin.interrupt_mode {
match interrupt_mode {
InterruptMode::None => {}
InterruptMode::RisingEdge => interrupt_value |= 1 << pin.pin_number * 2,
InterruptMode::FallingEdge => interrupt_mask |= 2 << pin.pin_number * 2,
InterruptMode::BothEdges => interrupt_value |= 3 << pin.pin_number * 2,
}
}
}
let payload = u32_vec_to_le_bytes(&[
dir_mask,
dir_value,
pull_mask,
pull_value,
interrupt_mask,
interrupt_value,
]);
let cmd = self.frame_cmd_packet(EtpOperations::GpioInit, payload);
self.send(cmd)?;
let response = self.receive(ETP_MESSAGE_HEADER_SIZE)?;
let status = response[ETP_MESSAGE_HEADER_STATUS_INDEX];
if status != 0 {
return Err(EtpError::InvalidEtpStatusCode(format!(
"GPIO Init failed with status: {}",
status
)));
}
self.gpio.pins = pins;
Ok(())
}
pub fn gpio_get_info(&self) -> EtpResult<GpioInfo> {
let cmd = self.frame_cmd_packet(
EtpOperations::GetGpioInfo,
vec![GpioInfoCommands::GpioPortCount as u8],
);
self.send(cmd)?;
let response = self.receive(ETP_MESSAGE_HEADER_SIZE + 2)?;
let port_count = response[ETP_MESSAGE_HEADER_SIZE + 1];
debug!("Port count: {}", port_count);
let cmd = self.frame_cmd_packet(
EtpOperations::GetGpioInfo,
vec![GpioInfoCommands::GpioPins as u8],
);
self.send(cmd)?;
let response = self.receive(ETP_MESSAGE_HEADER_SIZE + 2)?;
let pin_info = response[(ETP_MESSAGE_HEADER_SIZE + 1)..].to_vec();
let pin_info = pin_info
.chunks(5)
.map(|chunk| {
let mut padded_chunk = chunk.to_vec();
while padded_chunk.len() < 5 {
padded_chunk.push(0);
}
padded_chunk
})
.collect::<Vec<_>>();
let mut gpio_info: GpioInfo = HashMap::new();
for chunk in pin_info.iter() {
let port = chunk[0];
let port: Port = if port == '_' as u8 {
None
} else {
Some(port as char)
};
let pin_mask: u32 = u32::from_le_bytes([chunk[1], chunk[2], chunk[3], chunk[4]]);
let pins: Vec<Pin> = util::mask_to_bits(pin_mask);
gpio_info.insert(port, pins);
}
Ok(gpio_info)
}
pub fn gpio_read(&mut self, port: Port, pins: Vec<Pin>) -> EtpResult<GpioPinsStatus> {
let mut pin_mask: u32 = 0;
for pin in pins.iter() {
pin_mask |= 1 << pin;
}
let port_char = match port {
Some(p) => p as u8,
None => '_' as u8,
};
let pin_mask_bytes = u32_vec_to_le_bytes(&[pin_mask]);
let payload = [
port_char,
pin_mask_bytes[0],
pin_mask_bytes[1],
pin_mask_bytes[2],
pin_mask_bytes[3],
];
let cmd = self.frame_cmd_packet(EtpOperations::GpioRead, payload.to_vec());
self.send(cmd)?;
let response = self.receive(ETP_MESSAGE_HEADER_SIZE + 9)?;
let status = response[ETP_MESSAGE_HEADER_STATUS_INDEX];
if status != 0 {
return Err(EtpError::InvalidEtpStatusCode(format!(
"GPIO Read failed with status: {}",
status
)));
}
let data = response[ETP_MESSAGE_HEADER_SIZE..].to_vec();
debug!("GPIO Read data: {:?}", data);
let requested_pins_status: [u8; 4] = [data[5], data[6], data[7], data[8]];
let pins_status = u32::from_le_bytes(requested_pins_status);
let mut gpio_pins_status: GpioPinsStatus = HashMap::new();
for pin in pins.iter() {
let pin_mask = 1 << pin;
let pin_status = if (pins_status & pin_mask) != 0 {
GpioPinStatus::High
} else {
GpioPinStatus::Low
};
gpio_pins_status.insert((port, *pin), pin_status);
}
Ok(gpio_pins_status)
}
pub fn gpio_write(&mut self, pins_status: GpioPinsStatus) -> EtpResult<()> {
let mut port_pins: HashMap<Port, Vec<(Pin, GpioPinStatus)>> = HashMap::new();
for ((port, pin), status) in pins_status.iter() {
port_pins
.entry(*port)
.or_insert_with(Vec::new)
.push((*pin, status.clone()));
}
for (port, pins) in port_pins.iter() {
let mut pin_mask: u32 = 0;
let mut pin_value: u32 = 0;
for (pin, status) in pins.iter() {
pin_mask |= 1 << pin;
if *status == GpioPinStatus::High {
pin_value |= 1 << pin;
}
}
let port_char = match port {
Some(p) => *p as u8,
None => '_' as u8,
};
let pin_mask_bytes = u32_vec_to_le_bytes(&[pin_mask]);
let pin_value_bytes = u32_vec_to_le_bytes(&[pin_value]);
let payload = [
port_char,
pin_mask_bytes[0],
pin_mask_bytes[1],
pin_mask_bytes[2],
pin_mask_bytes[3],
pin_value_bytes[0],
pin_value_bytes[1],
pin_value_bytes[2],
pin_value_bytes[3],
];
let cmd = self.frame_cmd_packet(EtpOperations::GpioWrite, payload.to_vec());
self.send(cmd)?;
let response = self.receive(ETP_MESSAGE_HEADER_SIZE)?;
let status = response[ETP_MESSAGE_HEADER_STATUS_INDEX];
if status != 0 {
return Err(EtpError::InvalidEtpStatusCode(format!(
"GPIO Write failed with status: {} for port {:?}",
status, port
)));
}
}
Ok(())
}
}