use crate::{
transport::{Deserialize, Serialize, Transport},
yellow_blocks::Address,
};
use casperfpga_derive::{address, CasperSerde};
use packed_struct::{prelude::*, PackedStruct, PackingResult};
use std::{
net::Ipv4Addr,
sync::{Arc, Mutex, Weak},
};
use thiserror::Error;
#[derive(PrimitiveEnum_u8, Debug, Copy, Clone)]
pub enum EthernetType {
OneGbE = 1,
TenGbE = 2,
TwentyFiveGbE = 3,
FortyGbE = 4,
HundredGbE = 5,
}
#[derive(PackedStruct, CasperSerde, Debug)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "4")]
#[address(0x0)]
pub struct CoreType {
#[packed_field(bits = "24")]
pub cpu_tx_enable: bool,
#[packed_field(bits = "16")]
pub cpu_rx_enable: bool,
#[packed_field(bytes = "1")]
pub revision: u8,
#[packed_field(bytes = "0", ty = "enum")]
pub core_type: EthernetType,
}
#[derive(PackedStruct, CasperSerde, Debug)]
#[address(0x4)]
pub struct BufferSizes {
#[packed_field(endian = "msb")]
pub tx_buf_max: u16,
#[packed_field(endian = "msb")]
pub rx_buf_max: u16,
}
#[derive(PackedStruct, CasperSerde, Debug)]
#[address(0x8)]
pub struct WordLengths {
#[packed_field(endian = "msb")]
pub tx_word_size: u16,
#[packed_field(endian = "msb")]
pub rx_word_size: u16,
}
#[derive(CasperSerde, Debug)]
#[address(0xC)]
pub struct MacAddress([u8; 6]);
impl PackedStruct for MacAddress {
type ByteArray = [u8; 8];
fn pack(&self) -> PackingResult<Self::ByteArray> {
let mut dest = [0u8; 8];
dest[2..].copy_from_slice(&self.0);
Ok(dest)
}
fn unpack(src: &Self::ByteArray) -> packed_struct::PackingResult<Self> {
Ok(MacAddress(src[2..].try_into().unwrap()))
}
}
macro_rules! ip_register {
($name:ident, $addr:literal) => {
#[derive(Debug, CasperSerde)]
#[address($addr)]
pub struct $name(pub Ipv4Addr);
impl PackedStruct for $name {
type ByteArray = [u8; 4];
fn pack(&self) -> PackingResult<Self::ByteArray> {
Ok(self.0.octets())
}
fn unpack(src: &Self::ByteArray) -> packed_struct::PackingResult<Self> {
Ok($name(Ipv4Addr::new(src[0], src[1], src[2], src[3])))
}
}
};
}
ip_register!(IpAddress, 0x14);
ip_register!(GatewayAddress, 0x18);
ip_register!(Netmask, 0x1C);
ip_register!(MulticastIp, 0x20);
ip_register!(MulticastMask, 0x24);
#[derive(PackedStruct, CasperSerde, Debug)]
#[address(0x28)]
pub struct BytesAvailable {
#[packed_field(endian = "msb")]
pub tx_size: u16,
#[packed_field(endian = "msb")]
pub rx_size: u16,
}
#[derive(PackedStruct, CasperSerde, Debug)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "4")]
#[address(0x2C)]
pub struct PromiscRstEn {
#[packed_field(bits = "4")]
pub soft_rst: bool,
#[packed_field(bits = "2")]
pub promisc: bool,
#[packed_field(bits = "0")]
pub enable: bool,
}
#[derive(PackedStruct, CasperSerde, Debug)]
#[address(0x30)]
pub struct Port {
#[packed_field(endian = "msb")]
pub port_mask: u16,
#[packed_field(endian = "msb")]
pub port: u16,
}
#[derive(PackedStruct, CasperSerde, Debug)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "8")]
#[address(0x34)]
pub struct Status {
#[packed_field(bits = "0")]
pub link_up: bool,
}
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Transport(#[from] crate::transport::Error),
}
#[derive(Debug)]
pub struct TenGbE<T> {
transport: Weak<Mutex<T>>,
name: String,
}
impl<T> TenGbE<T>
where
T: Transport,
{
#[must_use]
pub fn new(transport: &Arc<Mutex<T>>, reg_name: &str) -> Self {
let transport = Arc::downgrade(transport);
Self {
transport,
name: reg_name.to_string(),
}
}
pub fn from_fpg(transport: Weak<Mutex<T>>, reg_name: &str) -> Result<Self, Error> {
Ok(Self {
transport,
name: reg_name.to_string(),
})
}
#[allow(clippy::missing_panics_doc)]
pub fn get_ip(&self) -> Result<Ipv4Addr, Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let ip: IpAddress = transport.read_addr(&self.name)?;
Ok(ip.0)
}
#[allow(clippy::missing_panics_doc)]
pub fn set_ip(&self, addr: Ipv4Addr) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
Ok(transport.write_addr(&self.name, &IpAddress(addr))?)
}
#[allow(clippy::missing_panics_doc)]
pub fn get_gateway(&self) -> Result<Ipv4Addr, Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let ip: GatewayAddress = transport.read_addr(&self.name)?;
Ok(ip.0)
}
#[allow(clippy::missing_panics_doc)]
pub fn set_gateway(&self, addr: Ipv4Addr) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
Ok(transport.write_addr(&self.name, &GatewayAddress(addr))?)
}
#[allow(clippy::missing_panics_doc)]
pub fn get_port(&self) -> Result<u16, Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let port: Port = transport.read_addr(&self.name)?;
Ok(port.port)
}
#[allow(clippy::missing_panics_doc)]
pub fn set_port(&self, port: u16) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
Ok(transport.write_addr(
&self.name,
&Port {
port_mask: 0xFF,
port,
},
)?)
}
#[allow(clippy::missing_panics_doc)]
pub fn get_netmask(&self) -> Result<Ipv4Addr, Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let ip: Netmask = transport.read_addr(&self.name)?;
Ok(ip.0)
}
#[allow(clippy::missing_panics_doc)]
pub fn set_netmask(&self, addr: Ipv4Addr) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
Ok(transport.write_addr(&self.name, &Netmask(addr))?)
}
#[allow(clippy::missing_panics_doc)]
pub fn get_mac(&self) -> Result<[u8; 6], Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let mac: MacAddress = transport.read_addr(&self.name)?;
Ok(mac.0)
}
#[allow(clippy::missing_panics_doc)]
pub fn set_mac(&self, mac: &[u8; 6]) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
Ok(transport.write_addr(&self.name, &MacAddress(*mac))?)
}
#[allow(clippy::missing_panics_doc)]
pub fn set_enable(&self, enabled: bool) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
Ok(transport.write_addr(
&self.name,
&PromiscRstEn {
soft_rst: false,
promisc: false,
enable: enabled,
},
)?)
}
#[allow(clippy::missing_panics_doc)]
pub fn toggle_reset(&self) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let mut pre: PromiscRstEn = transport.read_addr(&self.name)?;
pre.soft_rst = false;
transport.write_addr(&self.name, &pre)?;
pre.soft_rst = true;
transport.write_addr(&self.name, &pre)?;
pre.soft_rst = false;
transport.write_addr(&self.name, &pre)?;
Ok(())
}
#[allow(clippy::missing_panics_doc)]
pub fn set_single_arp_entry(&self, ip: Ipv4Addr, mac: &[u8; 6]) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let offset = 0x1000 + 8 * (*ip.octets().last().unwrap()) as usize;
transport.write(&self.name, offset, &MacAddress(*mac))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{core::Register, transport::mock::Mock};
use std::{collections::HashMap, sync::Arc};
#[test]
fn test_set_single_arp_entry() {
let transport = Mock::new(HashMap::from([(
"gbe0".into(),
Register {
addr: 0,
length: 12411, },
)]));
let transport = Arc::new(Mutex::new(transport));
let gbe0 = TenGbE::new(&transport, "gbe0");
gbe0.set_single_arp_entry(
"192.168.0.1".parse().unwrap(),
&[0xDE, 0xAD, 0xBE, 0xEF, 0xB0, 0xBA],
)
.unwrap();
let bytes = transport
.lock()
.unwrap()
.read_n_bytes("gbe0", 4104, 8) .unwrap();
assert_eq!(vec![0, 0, 0xDE, 0xAD, 0xBE, 0xEF, 0xB0, 0xBA], bytes);
}
}