From d34ef053fd499c6302fb955498d6d91913a91a58 Mon Sep 17 00:00:00 2001 From: Peter Hartley Date: Mon, 15 Apr 2024 10:47:53 +0100 Subject: [PATCH] Allow interrupt-driven MACRAW operation This commit adds methods to RawDevice that enable interrupt-driven operation. The enable_interrupt() method sets up SIMR so that socket-level (internal) interrupts on Socket 0 cause chip-level (external) interrupts (and as a convenience also sets S0_IR as required). The disable_interrupt() method reverses those changes. The clear_interrupt() method acknowledges all interrupts and is intended to be called from the interrupt handler (or from thread mode soon afterwards). There is no change to existing functionality or operation if enable_interrupt() is never called. I did see PR#34 before filing this, but that change is focused on TCP and UDP sockets, and my use case is MACRAW mode. Tested on a W5500-Pico-EVB board with the RP2040 successfully receiving and acting on active-low GPIO interrupts from W5500 via the INTn signal on W5500 pin 36. --- src/raw_device.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ src/register.rs | 3 +++ 2 files changed, 45 insertions(+) diff --git a/src/raw_device.rs b/src/raw_device.rs index 25b09a9..c9e1224 100644 --- a/src/raw_device.rs +++ b/src/raw_device.rs @@ -39,6 +39,48 @@ impl RawDevice { Ok(Self { bus, raw_socket }) } + /// Enable one or more interrupts + /// + /// # Args + /// * `which` - The interrupts to enable; see `register::socketn::Interrupt` + /// For instance, pass `Interrupt::Receive` to get interrupts + /// on packet reception only. + /// + pub fn enable_interrupt(&mut self, which: u8) -> Result<(), SpiBus::Error> { + self.raw_socket.set_interrupt_mask(&mut self.bus, which)?; + self.bus.write_frame( + register::COMMON, + register::common::SOCKET_INTERRUPT_MASK, + &[1], + )?; + Ok(()) + } + + /// Clear pending interrupts + /// + /// If using RTIC or similar, this should be called from the + /// interrupt handler. If not (i.e., if there's concern that this + /// use of the SPI bus will clobber someone else's use), then you + /// can mask the interrupt *at microcontroller level* in the + /// interrupt handler, then call this from thread mode before + /// unmasking again. + pub fn clear_interrupt(&mut self) -> Result<(), SpiBus::Error> { + self.raw_socket + .reset_interrupt(&mut self.bus, register::socketn::Interrupt::All) + } + + /// Disable all interrupts + /// + pub fn disable_interrupt(&mut self) -> Result<(), SpiBus::Error> { + self.bus.write_frame( + register::COMMON, + register::common::SOCKET_INTERRUPT_MASK, + &[0], + )?; + self.raw_socket.set_interrupt_mask(&mut self.bus, 0xFF)?; + Ok(()) + } + /// Read an ethernet frame from the device. /// /// # Args diff --git a/src/register.rs b/src/register.rs index 3a1acfa..03b885b 100644 --- a/src/register.rs +++ b/src/register.rs @@ -22,6 +22,9 @@ pub mod common { /// Register: INTLEVEL (Interrupt Low Level Timer Register) [R/W] [0x0013 – 0x0014] [0x0000] pub const INTERRUPT_TIMER: u16 = 0x13; + /// Register: SIMR (Socket Interrupt Mask Register) [R/W] [0x0018] [0x00] + pub const SOCKET_INTERRUPT_MASK: u16 = 0x18; + /// Register: RTR (Retry Time-value Register) [R/W] [0x0019 – 0x001A] [0x07D0] pub const RETRY_TIME: u16 = 0x19;