diff --git a/src/lib.rs b/src/lib.rs index 237b72e..a81bf7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ pub mod bus; mod device; mod host; pub mod net; +pub mod raw_device; pub mod register; mod socket; pub mod tcp; diff --git a/src/raw_device.rs b/src/raw_device.rs new file mode 100644 index 0000000..89c9cd3 --- /dev/null +++ b/src/raw_device.rs @@ -0,0 +1,111 @@ +use crate::{bus::Bus, register, socket::Socket, uninitialized_device::InitializeError}; + +/// The W5500 operating in MACRAW mode to send and receive ethernet frames. +pub struct RawDevice { + bus: SpiBus, + raw_socket: Socket, +} + +impl RawDevice { + /// Create the raw device. + /// + /// # Note + /// The device is configured with MAC filtering enabled. + /// + /// # Args + /// * `bus` - The bus to communicate with the device. + pub(crate) fn new(mut bus: SpiBus) -> Result> { + let raw_socket = Socket::new(0); + + // Configure the chip in MACRAW mode with MAC filtering. + let mode: u8 = (1 << 7) | (register::socketn::Protocol::MacRaw as u8); + + bus.write_frame(raw_socket.register(), register::socketn::MODE, &[mode])?; + raw_socket.command(&mut bus, register::socketn::Command::Open)?; + + Ok(Self { bus, raw_socket }) + } + + /// Read an ethernet frame from the device. + /// + /// # Args + /// * `frame` - The location to store the received frame + /// + /// # Returns + /// The number of bytes read into the provided frame buffer. + pub fn read_frame(&mut self, frame: &mut [u8]) -> Result { + if !self + .raw_socket + .has_interrupt(&mut self.bus, register::socketn::Interrupt::Receive)? + { + return Ok(0); + } + + let rx_size = self.raw_socket.get_receive_size(&mut self.bus)? as usize; + + let read_buffer = if rx_size > frame.len() { + frame + } else { + &mut frame[..rx_size] + }; + + // Read from the RX ring buffer. + let read_pointer = self.raw_socket.get_rx_read_pointer(&mut self.bus)?; + self.bus + .read_frame(self.raw_socket.rx_buffer(), read_pointer, read_buffer)?; + self.raw_socket.set_rx_read_pointer( + &mut self.bus, + read_pointer.wrapping_add(read_buffer.len() as u16), + )?; + + // Register the reception as complete. + self.raw_socket + .command(&mut self.bus, register::socketn::Command::Receive)?; + self.raw_socket + .reset_interrupt(&mut self.bus, register::socketn::Interrupt::Receive)?; + + Ok(read_buffer.len()) + } + + /// Write an ethernet frame to the device. + /// + /// # Args + /// * `frame` - The ethernet frame to transmit. + /// + /// # Returns + /// The number of bytes successfully transmitted from the provided buffer. + pub fn write_frame(&mut self, frame: &[u8]) -> Result { + let max_size = self.raw_socket.get_tx_free_size(&mut self.bus)? as usize; + + let write_data = if frame.len() < max_size { + frame + } else { + &frame[..max_size] + }; + + // Append the data to the write buffer after the current write pointer. + let write_pointer = self.raw_socket.get_tx_write_pointer(&mut self.bus)?; + + // Write data into the buffer and update the writer pointer. + self.bus + .write_frame(self.raw_socket.tx_buffer(), write_pointer, write_data)?; + self.raw_socket.set_tx_write_pointer( + &mut self.bus, + write_pointer.wrapping_add(write_data.len() as u16), + )?; + + // Wait for the socket transmission to complete. + self.raw_socket + .reset_interrupt(&mut self.bus, register::socketn::Interrupt::SendOk)?; + + self.raw_socket + .command(&mut self.bus, register::socketn::Command::Send)?; + + while !self + .raw_socket + .has_interrupt(&mut self.bus, register::socketn::Interrupt::SendOk)? + {} + + Ok(write_data.len()) + } +} diff --git a/src/register.rs b/src/register.rs index 1ae2765..5acb049 100644 --- a/src/register.rs +++ b/src/register.rs @@ -167,6 +167,7 @@ pub mod socketn { Closed = 0b00, Tcp = 0b01, Udp = 0b10, + MacRaw = 0b100, } pub const COMMAND: u16 = 0x01; #[repr(u8)] diff --git a/src/uninitialized_device.rs b/src/uninitialized_device.rs index c27d80a..96ab3b1 100644 --- a/src/uninitialized_device.rs +++ b/src/uninitialized_device.rs @@ -5,6 +5,7 @@ use embedded_nal::Ipv4Addr; use crate::bus::{Bus, FourWire, ThreeWire}; use crate::device::Device; use crate::host::{Dhcp, Host, Manual}; +use crate::raw_device::RawDevice; use crate::register; use crate::{MacAddress, Mode}; @@ -98,6 +99,15 @@ impl UninitializedDevice { Ok(Device::new(self.bus, host)) } + pub fn initialize_macraw( + mut self, + mac: MacAddress, + ) -> Result, InitializeError> { + self.bus + .write_frame(register::COMMON, register::common::MAC, &mac.octets)?; + RawDevice::new(self.bus) + } + #[cfg(not(feature = "no-chip-version-assertion"))] fn assert_chip_version( &mut self,