w5500-ll 0.10.4

Low level driver for the Wiznet W5500 internet offload chip.
Documentation
//! Register structures.

use crate::specifiers::{DuplexStatus, LinkStatus, OperationMode, Protocol, SpeedStatus};

macro_rules! impl_boilerplate_for {
    ($REG:ident) => {
        impl From<u8> for $REG {
            fn from(val: u8) -> Self {
                Self(val)
            }
        }

        impl From<$REG> for u8 {
            fn from(val: $REG) -> u8 {
                val.0
            }
        }

        impl Default for $REG {
            fn default() -> Self {
                Self::DEFAULT
            }
        }
    };
}

/// Mode register (MR).
///
/// Used for software reset, and controlling modes of operation.
///
/// This is used by the [`Registers::mr`] and [`Registers::set_mr`] methods.
///
/// [`Registers::mr`]: crate::Registers::mr
/// [`Registers::set_mr`]: crate::Registers::set_mr
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Mode(u8);
impl_boilerplate_for!(Mode);

impl Mode {
    /// Mode register reset value.
    pub const RESET: u8 = 0x00;

    /// Default value.
    ///
    /// This is the same as `default`, but as a `const` value.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Mode;
    ///
    /// assert_eq!(Mode::DEFAULT, Mode::default());
    /// ```
    pub const DEFAULT: Self = Self(Self::RESET);

    /// Bit offset for the `RST` field.
    pub const RST_OFFSET: u8 = 7;
    /// Bit offset for the `WOL` field.
    pub const WOL_OFFSET: u8 = 5;
    /// Bit offset for the `PB` field.
    pub const PB_OFFSET: u8 = 4;
    /// Bit offset for the `PPPoE` field.
    pub const PPPOE_OFFSET: u8 = 3;
    /// Bit offset for the `FARP` field.
    pub const FARP_OFFSET: u8 = 1;

    /// Bit mask for the `RST` field.
    pub const RST_MASK: u8 = 1 << Self::RST_OFFSET;
    /// Bit mask for the `WOL` field.
    pub const WOL_MASK: u8 = 1 << Self::WOL_OFFSET;
    /// Bit mask for the `PB` field.
    pub const PB_MASK: u8 = 1 << Self::PB_OFFSET;
    /// Bit mask for the `PPPoE` field.
    pub const PPPOE_MASK: u8 = 1 << Self::PPPOE_OFFSET;
    /// Bit mask for the `FARP` field.
    pub const FARP_MASK: u8 = 1 << Self::FARP_OFFSET;

    /// Set the software reset bit to `1`.
    ///
    /// When reset all internal registers will be initialized.
    #[must_use = "rst returns a modified Mode"]
    pub const fn rst(mut self) -> Self {
        self.0 |= Self::RST_MASK;
        self
    }

    /// Wake on LAN.
    ///
    /// If WOL mode is enabled and the received magic packet over
    /// UDP has been normally processed, the interrupt pin (INTn) asserts to low.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Mode;
    ///
    /// let mr: Mode = Mode::DEFAULT;
    /// assert!(!mr.wol_enabled());
    /// let mr: Mode = mr.enable_wol();
    /// assert!(mr.wol_enabled());
    /// let mr: Mode = mr.disable_wol();
    /// assert!(!mr.wol_enabled());
    /// ```
    pub const fn wol_enabled(&self) -> bool {
        self.0 & Self::WOL_MASK != 0
    }

    /// Enable wake on LAN.
    #[must_use = "enable_wol returns a modified Mode"]
    pub const fn enable_wol(mut self) -> Self {
        self.0 |= Self::WOL_MASK;
        self
    }

    /// Disable wake on LAN.
    #[must_use = "disable_wol returns a modified Mode"]
    pub const fn disable_wol(mut self) -> Self {
        self.0 &= !Self::WOL_MASK;
        self
    }

    /// Ping block mode.
    ///
    /// If enabled it blocks responses to ping requests.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Mode;
    ///
    /// let mr: Mode = Mode::DEFAULT;
    /// assert!(!mr.pb_enabled());
    /// let mr: Mode = mr.enable_pb();
    /// assert!(mr.pb_enabled());
    /// let mr: Mode = mr.disable_pb();
    /// assert!(!mr.pb_enabled());
    /// ```
    pub const fn pb_enabled(&self) -> bool {
        self.0 & Self::PB_MASK != 0
    }

    /// Enable ping block.
    #[must_use = "enable_pb returns a modified Mode"]
    pub const fn enable_pb(mut self) -> Self {
        self.0 |= Self::PB_MASK;
        self
    }

    /// Disable ping block.
    #[must_use = "disable_pb returns a modified Mode"]
    pub const fn disable_pb(mut self) -> Self {
        self.0 &= !Self::PB_MASK;
        self
    }

    /// PPPoE mode.
    ///
    /// If you use ADSL this should be enabled.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Mode;
    ///
    /// let mr: Mode = Mode::DEFAULT;
    /// assert!(!mr.pppoe_enabled());
    /// let mr: Mode = mr.enable_pppoe();
    /// assert!(mr.pppoe_enabled());
    /// let mr: Mode = mr.disable_pppoe();
    /// assert!(!mr.pppoe_enabled());
    /// ```
    pub const fn pppoe_enabled(&self) -> bool {
        self.0 & Self::PPPOE_MASK != 0
    }

    /// Enable PPPoE mode.
    #[must_use = "enable_pppoe returns a modified Mode"]
    pub const fn enable_pppoe(mut self) -> Self {
        self.0 |= Self::PPPOE_MASK;
        self
    }

    /// Disable PPPoE mode.
    #[must_use = "disable_pppoe returns a modified Mode"]
    pub const fn disable_pppoe(mut self) -> Self {
        self.0 &= !Self::PPPOE_MASK;
        self
    }

    /// Force ARP.
    ///
    /// When enabled it forces sending ARP request whenever data is sent.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Mode;
    ///
    /// let mr: Mode = Mode::DEFAULT;
    /// assert!(!mr.farp_enabled());
    /// let mr: Mode = mr.enable_farp();
    /// assert!(mr.farp_enabled());
    /// let mr: Mode = mr.disable_farp();
    /// assert!(!mr.farp_enabled());
    /// ```
    pub const fn farp_enabled(&self) -> bool {
        self.0 & Self::FARP_MASK != 0
    }

    /// Enable force ARP.
    #[must_use = "enable_farp returns a modified Mode"]
    pub const fn enable_farp(mut self) -> Self {
        self.0 |= Self::FARP_MASK;
        self
    }

    /// Disable force ARP.
    #[must_use = "disable_farp returns a modified Mode"]
    pub const fn disable_farp(mut self) -> Self {
        self.0 &= !Self::FARP_MASK;
        self
    }
}

impl ::core::fmt::Display for Mode {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        f.debug_struct("Mode")
            .field("wol_enabled", &self.wol_enabled())
            .field("pb_enabled", &self.pb_enabled())
            .field("pppoe_enabled", &self.pppoe_enabled())
            .field("farp_enabled", &self.farp_enabled())
            .finish()
    }
}

#[cfg(feature = "defmt")]
impl defmt::Format for Mode {
    fn format(&self, fmt: defmt::Formatter) {
        defmt::write!(
            fmt,
            "Mode {{ wol_enabled: {}, pb_enabled: {}, pppoe_enabled: {}, farp_enabled: {} }}",
            self.wol_enabled(),
            self.pb_enabled(),
            self.pppoe_enabled(),
            self.farp_enabled(),
        );
    }
}

/// Interrupt and interrupt mask register (IR and IMR).
///
/// When used for interrupt masking:
/// * `false`: Interrupt is disabled.
/// * `true`: Interrupt is enabled.
///
/// When used for reading interrupt status:
/// * `false`: Interrupt is not raised.
/// * `true`: Interrupt is raised.
///
/// This is used by these methods:
/// * [`Registers::ir`]
/// * [`Registers::set_ir`]
/// * [`Registers::imr`]
/// * [`Registers::set_imr`]
///
/// [`Registers::ir`]: crate::Registers::ir
/// [`Registers::set_ir`]: crate::Registers::set_ir
/// [`Registers::imr`]: crate::Registers::imr
/// [`Registers::set_imr`]: crate::Registers::set_imr
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Interrupt(u8);
impl_boilerplate_for!(Interrupt);

impl Interrupt {
    /// Interrupt and interrupt mask reset value.
    pub const RESET: u8 = 0x00;

    /// Default value.
    ///
    /// This is the same as `default`, but as a `const` value.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Interrupt;
    ///
    /// assert_eq!(Interrupt::DEFAULT, Interrupt::default());
    /// ```
    pub const DEFAULT: Self = Self(Self::RESET);

    /// Bit offset for the `CONFLICT` field.
    pub const CONFLICT_OFFSET: u8 = 7;
    /// Bit offset for the `UNREACH` field.
    pub const UNREACH_OFFSET: u8 = 6;
    /// Bit offset for the `PPPoE` field.
    pub const PPPOE_OFFSET: u8 = 5;
    /// Bit offset for the `MP` field.
    pub const MP_OFFSET: u8 = 4;

    /// Bit mask for the `CONFLICT` field.
    pub const CONFLICT_MASK: u8 = 1 << Self::CONFLICT_OFFSET;
    /// Bit mask for the `UNREACH` field.
    pub const UNREACH_MASK: u8 = 1 << Self::UNREACH_OFFSET;
    /// Bit mask for the `PPPoE` field.
    pub const PPPOE_MASK: u8 = 1 << Self::PPPOE_OFFSET;
    /// Bit mask for the `MP` field.
    pub const MP_MASK: u8 = 1 << Self::MP_OFFSET;

    /// Get the value of the IP conflict interrupt.
    ///
    /// This interrupt is set when our source IP is the same as the sender IP
    /// in the received ARP request.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Interrupt;
    ///
    /// let ir: Interrupt = Interrupt::DEFAULT;
    /// assert!(!ir.conflict());
    /// let ir: Interrupt = ir.set_conflict();
    /// assert!(ir.conflict());
    /// let ir: Interrupt = ir.clear_conflict();
    /// assert!(!ir.conflict());
    /// ```
    pub const fn conflict(&self) -> bool {
        self.0 & Self::CONFLICT_MASK != 0
    }

    /// Set the IP conflict bit.
    #[must_use = "set_conflict returns a modified Interrupt"]
    pub const fn set_conflict(mut self) -> Self {
        self.0 |= Self::CONFLICT_MASK;
        self
    }

    /// Clear the IP conflict bit.
    #[must_use = "clear_conflict returns a modified Interrupt"]
    pub const fn clear_conflict(mut self) -> Self {
        self.0 &= !Self::CONFLICT_MASK;
        self
    }

    /// Get the destination unreachable interrupt.
    ///
    /// This interrupt is set when receiving the ICMP
    /// (destination port unreachable) packet.
    ///
    /// When this interrupt is set destination information such as the IP
    /// address and port number may be checked with the corresponding [UIPR] and
    /// [UPORTR] registers.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Interrupt;
    ///
    /// let ir: Interrupt = Interrupt::DEFAULT;
    /// assert!(!ir.unreach());
    /// let ir: Interrupt = ir.set_unreach();
    /// assert!(ir.unreach());
    /// let ir: Interrupt = ir.clear_unreach();
    /// assert!(!ir.unreach());
    /// ```
    ///
    /// [UIPR]: crate::Registers::uipr
    /// [UPORTR]: crate::Registers::uportr
    pub const fn unreach(&self) -> bool {
        self.0 & Self::UNREACH_MASK != 0
    }

    /// Set the destination unreachable bit.
    #[must_use = "set_unreach returns a modified Interrupt"]
    pub const fn set_unreach(mut self) -> Self {
        self.0 |= Self::UNREACH_MASK;
        self
    }

    /// Clear the destination unreachable bit.
    #[must_use = "clear_unreach returns a modified Interrupt"]
    pub const fn clear_unreach(mut self) -> Self {
        self.0 &= !Self::UNREACH_MASK;
        self
    }

    /// Get the PPPoE connection close interrupt.
    ///
    /// This interrupt is set when PPPoE is disconnected during PPPoE.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Interrupt;
    ///
    /// let ir: Interrupt = Interrupt::DEFAULT;
    /// assert!(!ir.pppoe());
    /// let ir: Interrupt = ir.set_pppoe();
    /// assert!(ir.pppoe());
    /// let ir: Interrupt = ir.clear_pppoe();
    /// assert!(!ir.pppoe());
    /// ```
    pub const fn pppoe(&self) -> bool {
        self.0 & Self::PPPOE_MASK != 0
    }

    /// Set the PPPoE connection close bit.
    #[must_use = "set_pppoe returns a modified Interrupt"]
    pub const fn set_pppoe(mut self) -> Self {
        self.0 |= Self::PPPOE_MASK;
        self
    }

    /// Clear the PPPoE connection close bit.
    #[must_use = "clear_pppoe returns a modified Interrupt"]
    pub const fn clear_pppoe(mut self) -> Self {
        self.0 &= !Self::PPPOE_MASK;
        self
    }

    /// Get the magic packet interrupt.
    ///
    /// This interrupt is set when wake on LAN is enabled, and the magic packet
    /// is received.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Interrupt;
    ///
    /// let ir: Interrupt = Interrupt::DEFAULT;
    /// assert!(!ir.mp());
    /// let ir: Interrupt = ir.set_mp();
    /// assert!(ir.mp());
    /// let ir: Interrupt = ir.clear_mp();
    /// assert!(!ir.mp());
    /// ```
    pub const fn mp(&self) -> bool {
        self.0 & Self::MP_MASK != 0
    }

    /// Set the magic packet bit.
    #[must_use = "set_mp returns a modified Interrupt"]
    pub const fn set_mp(mut self) -> Self {
        self.0 |= Self::MP_MASK;
        self
    }

    /// Clear the magic packet bit.
    #[must_use = "clear_mp returns a modified Interrupt"]
    pub const fn clear_mp(mut self) -> Self {
        self.0 &= !Self::MP_MASK;
        self
    }
}

impl ::core::fmt::Display for Interrupt {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        f.debug_struct("Interrupt")
            .field("conflict", &self.conflict())
            .field("unreach", &self.unreach())
            .field("pppoe", &self.pppoe())
            .field("mp", &self.mp())
            .finish()
    }
}

#[cfg(feature = "defmt")]
impl defmt::Format for Interrupt {
    fn format(&self, fmt: defmt::Formatter) {
        defmt::write!(
            fmt,
            "Interrupt {{ conflict: {}, unreach: {}, pppoe: {}, mp: {} }}",
            self.conflict(),
            self.unreach(),
            self.pppoe(),
            self.mp(),
        );
    }
}

/// PHY configuration register (PHYCFGR).
///
/// Used for:
/// * PHY reset.
/// * PHY operation modes.
/// * PHY status.
///
/// This is used by the [`Registers::phycfgr`] and
/// [`Registers::set_phycfgr`] methods.
///
/// [`Registers::phycfgr`]: crate::Registers::phycfgr
/// [`Registers::set_phycfgr`]: crate::Registers::set_phycfgr
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct PhyCfg(u8);
impl_boilerplate_for!(PhyCfg);

impl PhyCfg {
    /// PHY configuration register reset value.
    pub const RESET: u8 = 0b10111000;

    /// Default value.
    ///
    /// This is the same as `default`, but as a `const` value.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::PhyCfg;
    ///
    /// assert_eq!(PhyCfg::DEFAULT, PhyCfg::default());
    /// ```
    pub const DEFAULT: Self = Self(Self::RESET);

    /// Bit offset for the `RST` field.
    pub const RST_OFFSET: u8 = 7;
    /// Bit offset for the `OPMD` field.
    pub const OPMD_OFFSET: u8 = 6;
    /// Bit offset for the `OPMDC` field.
    pub const OPMDC_OFFSET: u8 = 3;
    /// Bit offset for the `DPX` field.
    pub const DPX_OFFSET: u8 = 2;
    /// Bit offset for the `SPD` field.
    pub const SPD_OFFSET: u8 = 1;
    /// Bit offset for the `LNK` field.
    pub const LNK_OFFSET: u8 = 0;

    /// Bit mask for the `RST` field.
    pub const RST_MASK: u8 = 1 << Self::RST_OFFSET;
    /// Bit mask for the `OPMD` field.
    pub const OPMD_MASK: u8 = 1 << Self::OPMD_OFFSET;
    /// Bit mask for the `OPMDC` field.
    pub const OPMDC_MASK: u8 = 0b111 << Self::OPMDC_OFFSET;
    /// Bit mask for the `DPX` field.
    pub const DPX_MASK: u8 = 1 << Self::DPX_OFFSET;
    /// Bit mask for the `SPD` field.
    pub const SPD_MASK: u8 = 1 << Self::SPD_OFFSET;
    /// Bit mask for the `LNK` field.
    pub const LNK_MASK: u8 = 1 << Self::LNK_OFFSET;

    /// Set the PHY reset bit to `0`, resetting the PHY.
    #[must_use = "rst returns a modified PhyCfg"]
    pub const fn rst(mut self) -> Self {
        self.0 &= !Self::RST_MASK;
        self
    }

    /// Get the PHY operation mode.
    ///
    /// * `true` configure PHY with software.
    /// * `false` (reset value) configure PHY with hardware.
    pub const fn opmd(&self) -> bool {
        self.0 & Self::OPMD_MASK != 0
    }

    /// Enable hardware configuration of the PHY operation mode.
    ///
    /// This uses the PMODE pins to select the PHY operation mode.
    ///
    /// | PMODE\[2\] | PMODE\[1\] | PMODE\[0\] | Description                                  |
    /// |------------|------------|------------|----------------------------------------------|
    /// | 0          | 0          | 0          | 10BT Half-duplex, Auto-negotiation disabled  |
    /// | 0          | 0          | 1          | 10BT Full-duplex, Auto-negotiation disabled  |
    /// | 0          | 1          | 0          | 100BT Half-duplex, Auto-negotiation disabled |
    /// | 0          | 1          | 1          | 100BT Full-duplex, Auto-negotiation disabled |
    /// | 1          | 0          | 0          | 100BT Half-duplex, Auto-negotiation enabled  |
    /// | 1          | 0          | 1          | Not used                                     |
    /// | 1          | 1          | 0          | Not used                                     |
    /// | 1          | 1          | 1          | All capable, Auto-negotiation enabled        |
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::PhyCfg;
    ///
    /// let phy_cfg: PhyCfg = PhyCfg::DEFAULT;
    /// assert!(!phy_cfg.opmd());
    /// let phy_cfg: PhyCfg = phy_cfg.software_op();
    /// assert!(phy_cfg.opmd());
    /// let phy_cfg: PhyCfg = phy_cfg.hardware_op();
    /// assert!(!phy_cfg.opmd());
    /// ```
    #[must_use = "hardware_op returns a modified PhyCfg"]
    pub const fn hardware_op(mut self) -> Self {
        self.0 &= !Self::OPMD_MASK;
        self
    }

    /// Enable software configuration of the PHY operation mode.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::PhyCfg;
    ///
    /// let phy_cfg: PhyCfg = PhyCfg::DEFAULT;
    /// assert!(!phy_cfg.opmd());
    /// let phy_cfg: PhyCfg = phy_cfg.software_op();
    /// assert!(phy_cfg.opmd());
    /// ```
    #[must_use = "software_op returns a modified PhyCfg"]
    pub const fn software_op(mut self) -> Self {
        self.0 |= Self::OPMD_MASK;
        self
    }

    /// Get the operation mode.
    ///
    /// This returns an `Err(u8)` with the opmdc bits if the opmdc bits do not
    /// match a valid operation mode.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::{OperationMode, PhyCfg};
    ///
    /// assert_eq!(PhyCfg::DEFAULT.opmdc(), OperationMode::Auto);
    /// ```
    pub const fn opmdc(&self) -> OperationMode {
        // from_raw masks the value
        OperationMode::from_raw(self.0 >> Self::OPMDC_OFFSET)
    }

    /// Set the PHY operation mode.
    ///
    /// Setting this will also call [`PhyCfg::software_op`] to enable the PHY
    /// configuration with the value stored in this register.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::{OperationMode, PhyCfg};
    ///
    /// let phy_cfg: PhyCfg = PhyCfg::DEFAULT;
    /// assert!(!phy_cfg.opmd());
    /// let phy_cfg: PhyCfg = phy_cfg.set_opmdc(OperationMode::PowerDown);
    /// assert!(phy_cfg.opmd());
    /// assert_eq!(phy_cfg.opmdc(), OperationMode::PowerDown);
    /// let phy_cfg: PhyCfg = phy_cfg.set_opmdc(OperationMode::Auto);
    /// assert_eq!(phy_cfg.opmdc(), OperationMode::Auto);
    /// ```
    #[must_use = "set_opmdc returns a modified PhyCfg"]
    pub const fn set_opmdc(mut self, mode: OperationMode) -> Self {
        self = self.software_op();
        self.0 &= !Self::OPMDC_MASK;
        self.0 |= (mode as u8) << Self::OPMDC_OFFSET;
        self
    }

    /// Get the duplex status.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::{DuplexStatus, PhyCfg};
    ///
    /// let phy_cfg: PhyCfg = PhyCfg::DEFAULT;
    /// assert_eq!(phy_cfg.dpx(), DuplexStatus::Half);
    /// ```
    pub const fn dpx(&self) -> DuplexStatus {
        match self.0 & Self::DPX_MASK == Self::DPX_MASK {
            true => DuplexStatus::Full,
            false => DuplexStatus::Half,
        }
    }

    /// Get the speed status.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::{PhyCfg, SpeedStatus};
    ///
    /// let phy_cfg: PhyCfg = PhyCfg::DEFAULT;
    /// assert_eq!(phy_cfg.spd(), SpeedStatus::Mbps10);
    /// ```
    pub const fn spd(&self) -> SpeedStatus {
        match self.0 & Self::SPD_MASK == Self::SPD_MASK {
            true => SpeedStatus::Mbps100,
            false => SpeedStatus::Mbps10,
        }
    }

    /// Get the link status.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::{LinkStatus, PhyCfg};
    ///
    /// let phy_cfg: PhyCfg = PhyCfg::DEFAULT;
    /// assert_eq!(phy_cfg.lnk(), LinkStatus::Down);
    /// ```
    pub const fn lnk(&self) -> LinkStatus {
        match self.0 & Self::LNK_MASK == Self::LNK_MASK {
            true => LinkStatus::Up,
            false => LinkStatus::Down,
        }
    }
}

impl ::core::fmt::Display for PhyCfg {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        f.debug_struct("PhyCfg")
            .field("opmd", &self.opmd())
            .field("opmdc", &self.opmdc())
            .field("dpx", &self.dpx())
            .field("spd", &self.spd())
            .field("lnk", &self.lnk())
            .finish()
    }
}

#[cfg(feature = "defmt")]
impl defmt::Format for PhyCfg {
    fn format(&self, fmt: defmt::Formatter) {
        defmt::write!(
            fmt,
            "PhyCfg {{ opmd: {}, opmdc: {}, dpx: {}, spd: {}, lnk: {} }}",
            self.opmd(),
            self.opmdc(),
            self.dpx(),
            self.spd(),
            self.lnk(),
        );
    }
}

/// Socket Mode Register (Sn_MR).
///
/// This is used by the [`Registers::sn_mr`] and
/// [`Registers::set_sn_mr`] methods.
///
/// [`Registers::set_sn_mr`]: crate::Registers::set_sn_mr
/// [`Registers::sn_mr`]: crate::Registers::sn_mr
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SocketMode(u8);
impl_boilerplate_for!(SocketMode);

impl SocketMode {
    /// Reset value of the socket mode register.
    pub const RESET: u8 = 0x00;

    /// Default value.
    ///
    /// This is the same as `default`, but as a `const` value.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketMode;
    ///
    /// assert_eq!(SocketMode::DEFAULT, SocketMode::default());
    /// ```
    pub const DEFAULT: Self = Self(Self::RESET);

    /// Bit offset for the `MULTI` field.
    pub const MULTI_OFFSET: u8 = 7;
    /// Bit offset for the `MFEN` field.
    pub const MFEN_OFFSET: u8 = 7;
    /// Bit offset for the `BCASTB` field.
    pub const BCASTB_OFFSET: u8 = 6;
    /// Bit offset for the `ND` field.
    pub const ND_OFFSET: u8 = 5;
    /// Bit offset for the `MC` field.
    pub const MC_OFFSET: u8 = 5;
    /// Bit offset for the `MMB` field.
    pub const MMB_OFFSET: u8 = 5;
    /// Bit offset for the `UCASTB` field.
    pub const UCASTB_OFFSET: u8 = 4;
    /// Bit offset for the `MIP6B` field.
    pub const MIP6B_OFFSET: u8 = 4;

    /// Bit mask for the `MULTI` field.
    pub const MULTI_MASK: u8 = 1 << Self::MULTI_OFFSET;
    /// Bit mask for the `MFEN` field.
    pub const MFEN_MASK: u8 = 1 << Self::MFEN_OFFSET;
    /// Bit mask for the `BCASTB` field.
    pub const BCASTB_MASK: u8 = 1 << Self::BCASTB_OFFSET;
    /// Bit mask for the `ND` field.
    pub const ND_MASK: u8 = 1 << Self::ND_OFFSET;
    /// Bit mask for the `MC` field.
    pub const MC_MASK: u8 = 1 << Self::MC_OFFSET;
    /// Bit mask for the `MMB` field.
    pub const MMB_MASK: u8 = 1 << Self::MMB_OFFSET;
    /// Bit mask for the `UCASTB` field.
    pub const UCASTB_MASK: u8 = 1 << Self::UCASTB_OFFSET;
    /// Bit mask for the `MIP6B` field.
    pub const MIP6B_MASK: u8 = 1 << Self::MIP6B_OFFSET;
    /// Bit mask for the `PROTOCOL` field.
    pub const PROTOCOL_MASK: u8 = 0xF;

    /// Get the protocol.
    ///
    /// This returns an `Err(u8)` with the protocol bits if the protocol bits
    /// do not match a valid protocol.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::{Protocol, SocketMode};
    ///
    /// let sn_mr: SocketMode = SocketMode::DEFAULT;
    /// assert_eq!(sn_mr.protocol(), Ok(Protocol::Closed));
    /// ```
    pub const fn protocol(&self) -> Result<Protocol, u8> {
        Protocol::from_raw(self.0 & Self::PROTOCOL_MASK)
    }

    /// Set the protocol.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::{Protocol, SocketMode};
    ///
    /// const SN_MR: SocketMode = SocketMode::DEFAULT.set_protocol(Protocol::Tcp);
    /// assert_eq!(SN_MR.protocol(), Ok(Protocol::Tcp));
    /// ```
    pub const fn set_protocol(mut self, protocol: Protocol) -> Self {
        self.0 = (self.0 & 0xF0) | ((protocol as u8) & 0xF);
        self
    }

    /// Multicasting.
    ///
    /// This applies only for a socket with the UDP protocol.
    ///
    /// To use multicasting [`Registers::sn_dipr`] and [`Registers::sn_dport`]
    /// should be configured with the multicast group IP and port number
    /// before the socket is opened.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketMode;
    ///
    /// let sn_mr: SocketMode = SocketMode::DEFAULT;
    /// assert!(!sn_mr.multi_enabled());
    /// let sn_mr: SocketMode = sn_mr.enable_multi();
    /// assert!(sn_mr.multi_enabled());
    /// let sn_mr: SocketMode = sn_mr.disable_multi();
    /// assert!(!sn_mr.multi_enabled());
    /// ```
    ///
    /// [`Registers::sn_dipr`]: crate::Registers::sn_dipr
    /// [`Registers::sn_dport`]: crate::Registers::sn_dport
    pub const fn multi_enabled(&self) -> bool {
        self.0 & Self::MULTI_MASK != 0
    }

    /// Enable multicasting.
    #[must_use = "enable_multi returns a modified SocketMode"]
    pub const fn enable_multi(mut self) -> Self {
        self.0 |= Self::MULTI_MASK;
        self
    }

    /// Disable multicasting.
    #[must_use = "disable_multi returns a modified SocketMode"]
    pub const fn disable_multi(mut self) -> Self {
        self.0 &= !Self::MULTI_MASK;
        self
    }

    /// MAC filter.
    ///
    /// This applies only for a socket with the MACRAW protocol.
    ///
    /// When enabled the W5500 can only receive broadcasting packets sent to
    /// itself.
    /// When disabled the W5500 can receive all packets.
    /// If you want to implement a hybrid TCP/IP stack it is recommended that
    /// this is enabled for reducing host overhead to process all the received
    /// packets.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketMode;
    ///
    /// let sn_mr: SocketMode = SocketMode::DEFAULT;
    /// assert!(!sn_mr.mfen_enabled());
    /// let sn_mr: SocketMode = sn_mr.enable_mfen();
    /// assert!(sn_mr.mfen_enabled());
    /// let sn_mr: SocketMode = sn_mr.disable_mfen();
    /// assert!(!sn_mr.mfen_enabled());
    /// ```
    pub const fn mfen_enabled(&self) -> bool {
        self.0 & Self::MFEN_MASK != 0
    }

    /// Enable MAC filter.
    #[must_use = "enable_mfen returns a modified SocketMode"]
    pub const fn enable_mfen(mut self) -> Self {
        self.0 |= Self::MFEN_MASK;
        self
    }

    /// Disable MAC filter.
    #[must_use = "disable_mfen returns a modified SocketMode"]
    pub const fn disable_mfen(mut self) -> Self {
        self.0 &= !Self::MFEN_MASK;
        self
    }

    /// Broadcast blocking.
    ///
    /// This applies only for a socket with the MACRAW or UDP protocol.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketMode;
    ///
    /// let sn_mr: SocketMode = SocketMode::DEFAULT;
    /// assert!(!sn_mr.bcastb_enabled());
    /// let sn_mr: SocketMode = sn_mr.enable_bcastb();
    /// assert!(sn_mr.bcastb_enabled());
    /// let sn_mr: SocketMode = sn_mr.disable_bcastb();
    /// assert!(!sn_mr.bcastb_enabled());
    /// ```
    pub const fn bcastb_enabled(&self) -> bool {
        self.0 & Self::BCASTB_MASK != 0
    }

    /// Enable broadcast blocking.
    #[must_use = "enable_bcastb returns a modified SocketMode"]
    pub const fn enable_bcastb(mut self) -> Self {
        self.0 |= Self::BCASTB_MASK;
        self
    }

    /// Disable broadcast blocking.
    #[must_use = "disable_bcastb returns a modified SocketMode"]
    pub const fn disable_bcastb(mut self) -> Self {
        self.0 &= !Self::BCASTB_MASK;
        self
    }

    /// Use no delayed ACK.
    ///
    /// This applies only for a socket with the TCP protocol.
    ///
    /// When enabled the ACK packet is sent without delay as soon as a data
    /// packet is received from a peer.
    /// When disabled the ACK packet is sent after waiting for the time
    /// configured by [`rtr`].
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketMode;
    ///
    /// let sn_mr: SocketMode = SocketMode::DEFAULT;
    /// assert!(!sn_mr.nd_enabled());
    /// let sn_mr: SocketMode = sn_mr.enable_nd();
    /// assert!(sn_mr.nd_enabled());
    /// let sn_mr: SocketMode = sn_mr.disable_nd();
    /// assert!(!sn_mr.nd_enabled());
    /// ```
    ///
    /// [`rtr`]: crate::Registers::rtr
    pub const fn nd_enabled(&self) -> bool {
        self.0 & Self::ND_MASK != 0
    }

    /// Disable no delayed ACK.
    #[must_use = "disable_nd returns a modified SocketMode"]
    pub const fn disable_nd(mut self) -> Self {
        self.0 &= !Self::ND_MASK;
        self
    }

    /// Enable no delayed ACK.
    #[must_use = "enable_nd returns a modified SocketMode"]
    pub const fn enable_nd(mut self) -> Self {
        self.0 |= Self::ND_MASK;
        self
    }

    /// Multicast IGMP version.
    ///
    /// This applies only for a socket with the UDP protocol.
    ///
    /// This field configures the version for IGMP messages (join/leave/report).
    ///
    /// * `false` IGMP version 2
    /// * `true` IGMP version 1
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketMode;
    ///
    /// let sn_mr: SocketMode = SocketMode::DEFAULT;
    /// assert!(!sn_mr.mc());
    /// let sn_mr: SocketMode = sn_mr.set_igmp_v1();
    /// assert!(sn_mr.mc());
    /// let sn_mr: SocketMode = sn_mr.set_igmp_v2();
    /// assert!(!sn_mr.mc());
    /// ```
    pub const fn mc(&self) -> bool {
        self.0 & Self::MC_MASK != 0
    }

    /// Set IGMP version 1.
    #[must_use = "set_igmp_v1 returns a modified SocketMode"]
    pub const fn set_igmp_v1(mut self) -> Self {
        self.0 |= Self::MC_MASK;
        self
    }

    /// Set IGMP version 2.
    #[must_use = "set_igmp_v2 returns a modified SocketMode"]
    pub const fn set_igmp_v2(mut self) -> Self {
        self.0 &= !Self::MC_MASK;
        self
    }

    /// Multicast blocking.
    ///
    /// This applies only for a socket with the [MACRAW] protocol.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketMode;
    ///
    /// let sn_mr: SocketMode = SocketMode::DEFAULT;
    /// assert!(!sn_mr.mmb_enabled());
    /// let sn_mr: SocketMode = sn_mr.enable_mmb();
    /// assert!(sn_mr.mmb_enabled());
    /// let sn_mr: SocketMode = sn_mr.disable_mmb();
    /// assert!(!sn_mr.mmb_enabled());
    /// ```
    ///
    /// [MACRAW]: crate::Protocol::Macraw
    pub const fn mmb_enabled(&self) -> bool {
        self.0 & Self::MMB_MASK != 0
    }

    /// Enable multicast blocking.
    #[must_use = "enable_mmb returns a modified SocketMode"]
    pub const fn enable_mmb(mut self) -> Self {
        self.0 |= Self::MMB_MASK;
        self
    }

    /// Disable multicast blocking.
    #[must_use = "disable_mmb returns a modified SocketMode"]
    pub const fn disable_mmb(mut self) -> Self {
        self.0 &= !Self::MMB_MASK;
        self
    }

    /// Unicast blocking enabled.
    ///
    /// This applies only for a socket with the UDP protocol.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketMode;
    ///
    /// let sn_mr: SocketMode = SocketMode::DEFAULT;
    /// assert!(!sn_mr.ucastb_enabled());
    /// let sn_mr: SocketMode = sn_mr.enable_ucastb();
    /// assert!(sn_mr.ucastb_enabled());
    /// let sn_mr: SocketMode = sn_mr.disable_ucastb();
    /// assert!(!sn_mr.ucastb_enabled());
    /// ```
    pub const fn ucastb_enabled(&self) -> bool {
        self.0 & Self::UCASTB_MASK != 0
    }

    /// Enable unicast blocking.
    #[must_use = "enable_ucastb returns a modified SocketMode"]
    pub const fn enable_ucastb(mut self) -> Self {
        self.0 |= Self::UCASTB_MASK;
        self
    }

    /// Disable unicast blocking.
    #[must_use = "disable_ucastb returns a modified SocketMode"]
    pub const fn disable_ucastb(mut self) -> Self {
        self.0 &= !Self::UCASTB_MASK;
        self
    }

    /// IPV6 packet blocking.
    ///
    /// This applies only for a socket with the [MACRAW] protocol.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketMode;
    ///
    /// let sn_mr: SocketMode = SocketMode::DEFAULT;
    /// assert!(!sn_mr.mip6b_enabled());
    /// let sn_mr: SocketMode = sn_mr.enable_mip6b();
    /// assert!(sn_mr.mip6b_enabled());
    /// let sn_mr: SocketMode = sn_mr.disable_mip6b();
    /// assert!(!sn_mr.mip6b_enabled());
    /// ```
    ///
    /// [MACRAW]: crate::Protocol::Macraw
    pub const fn mip6b_enabled(&self) -> bool {
        self.0 & Self::MIP6B_MASK != 0
    }

    /// Enable IPV6 packet blocking.
    #[must_use = "enable_mip6b returns a modified SocketMode"]
    pub const fn enable_mip6b(mut self) -> Self {
        self.0 |= Self::MIP6B_MASK;
        self
    }

    /// Disable IPV6 packet blocking.
    #[must_use = "disable_mip6b returns a modified SocketMode"]
    pub const fn disable_mip6b(mut self) -> Self {
        self.0 &= !Self::MIP6B_MASK;
        self
    }
}

impl ::core::fmt::Display for SocketMode {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        f.debug_struct("SocketMode")
            .field("protocol", &self.protocol())
            .field("multi_enabled", &self.multi_enabled())
            .field("mfen_enabled", &self.mfen_enabled())
            .field("bcastb_enabled", &self.bcastb_enabled())
            .field("nd_enabled", &self.nd_enabled())
            .field("mc", &self.mc())
            .field("mmb_enabled", &self.mmb_enabled())
            .field("ucastb_enabled", &self.ucastb_enabled())
            .field("mip6b_enabled", &self.mip6b_enabled())
            .finish()
    }
}

#[cfg(feature = "defmt")]
impl defmt::Format for SocketMode {
    fn format(&self, fmt: defmt::Formatter) {
        defmt::write!(
            fmt,
            "SocketMode {{ protocol: {}, multi_enabled: {}, mfen_enabled: {}, bcastb_enabled: {}, nd_enabled: {}, mc: {}, mmb_enabled: {}, ucastb_enabled: {}, mip6b_enabled: {} }}",
            self.protocol(),
            self.multi_enabled(),
            self.mfen_enabled(),
            self.bcastb_enabled(),
            self.nd_enabled(),
            self.mc(),
            self.mmb_enabled(),
            self.ucastb_enabled(),
            self.mip6b_enabled(),
        );
    }
}

/// Socket Interrupt Register (Sn_IR).
///
/// Indicated the socket status, such as connection, termination,
/// receiving data, and timeout.
///
/// This is used by the [`Registers::sn_ir`] and
/// [`Registers::set_sn_ir`] methods.
///
/// [`Registers::sn_ir`]: crate::Registers::sn_ir
/// [`Registers::set_sn_ir`]: crate::Registers::set_sn_ir
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SocketInterrupt(u8);
impl_boilerplate_for!(SocketInterrupt);

impl SocketInterrupt {
    /// Socket interrupt status register (Sn_IR) reset value.
    pub const RESET: u8 = 0x00;

    /// Default value.
    ///
    /// This is the same as `default`, but as a `const` value.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterrupt;
    ///
    /// assert_eq!(SocketInterrupt::DEFAULT, SocketInterrupt::default());
    /// ```
    pub const DEFAULT: Self = Self(Self::RESET);

    /// Bit offset for the `CON` field.
    pub const CON_OFFSET: u8 = 0;
    /// Bit offset for the `DISCON` field.
    pub const DISCON_OFFSET: u8 = 1;
    /// Bit offset for the `RECV` field.
    pub const RECV_OFFSET: u8 = 2;
    /// Bit offset for the `TIMEOUT` field.
    pub const TIMEOUT_OFFSET: u8 = 3;
    /// Bit offset for the `SENDOK` field.
    pub const SENDOK_OFFSET: u8 = 4;

    /// Bit mask for the `CON` field.
    pub const CON_MASK: u8 = 1 << Self::CON_OFFSET;
    /// Bit mask for the `DISCON` field.
    pub const DISCON_MASK: u8 = 1 << Self::DISCON_OFFSET;
    /// Bit mask for the `RECV` field.
    pub const RECV_MASK: u8 = 1 << Self::RECV_OFFSET;
    /// Bit mask for the `TIMEOUT` field.
    pub const TIMEOUT_MASK: u8 = 1 << Self::TIMEOUT_OFFSET;
    /// Bit mask for the `SENDOK` field.
    pub const SENDOK_MASK: u8 = 1 << Self::SENDOK_OFFSET;

    const ALL_MASK: u8 = Self::CON_MASK
        | Self::DISCON_MASK
        | Self::RECV_MASK
        | Self::TIMEOUT_MASK
        | Self::SENDOK_MASK;

    /// Get the value of the `CON` interrupt.
    ///
    /// This is issued once when the connection with the peer is successful.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterrupt;
    ///
    /// let sir: SocketInterrupt = SocketInterrupt::DEFAULT;
    /// assert!(!sir.con_raised());
    /// # assert!(sir.clear_con().con_raised());
    /// ```
    pub const fn con_raised(&self) -> bool {
        self.0 & Self::CON_MASK != 0
    }

    /// Clear the `CON` interrupt by writing `1`.
    #[must_use = "clear_con returns a modified SocketInterrupt"]
    pub const fn clear_con(mut self) -> Self {
        self.0 |= Self::CON_MASK;
        self
    }

    /// Get the value of the `DISCON` interrupt.
    ///
    /// This is issued when FIN or FIN/ACK packet is received from a peer.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterrupt;
    ///
    /// let sir: SocketInterrupt = SocketInterrupt::DEFAULT;
    /// assert!(!sir.discon_raised());
    /// # assert!(sir.clear_discon().discon_raised());
    /// ```
    pub const fn discon_raised(&self) -> bool {
        self.0 & Self::DISCON_MASK != 0
    }

    /// Clear the `DISCON` interrupt by writing `1`.
    #[must_use = "clear_discon returns a modified SocketInterrupt"]
    pub const fn clear_discon(mut self) -> Self {
        self.0 |= Self::DISCON_MASK;
        self
    }

    /// Get the value of the `RECV` interrupt.
    ///
    /// This is issued whenever data is received from a peer.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterrupt;
    ///
    /// let sir: SocketInterrupt = SocketInterrupt::DEFAULT;
    /// assert!(!sir.recv_raised());
    /// # assert!(sir.clear_recv().recv_raised());
    /// ```
    pub const fn recv_raised(&self) -> bool {
        self.0 & Self::RECV_MASK != 0
    }

    /// Clear the `RECV` interrupt by writing `1`.
    #[must_use = "clear_recv returns a modified SocketInterrupt"]
    pub const fn clear_recv(mut self) -> Self {
        self.0 |= Self::RECV_MASK;
        self
    }

    /// Get the value of the `TIMEOUT` interrupt.
    ///
    /// This is issued when ARP<sub>TO</sub> or TCP<sub>TO</sub> occurs.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterrupt;
    ///
    /// let sir: SocketInterrupt = SocketInterrupt::DEFAULT;
    /// assert!(!sir.timeout_raised());
    /// # assert!(sir.clear_timeout().timeout_raised());
    /// ```
    pub const fn timeout_raised(&self) -> bool {
        self.0 & Self::TIMEOUT_MASK != 0
    }

    /// Clear the `TIMEOUT` interrupt by writing `1`.
    #[must_use = "clear_timeout returns a modified SocketInterrupt"]
    pub const fn clear_timeout(mut self) -> Self {
        self.0 |= Self::TIMEOUT_MASK;
        self
    }

    /// Get the value of the `SENDOK` interrupt.
    ///
    /// This is issued when [SEND] command is completed.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterrupt;
    ///
    /// let sir: SocketInterrupt = SocketInterrupt::DEFAULT;
    /// assert!(!sir.sendok_raised());
    /// # assert!(sir.clear_sendok().sendok_raised());
    /// ```
    ///
    /// [SEND]: crate::SocketCommand::Send
    pub const fn sendok_raised(&self) -> bool {
        self.0 & Self::SENDOK_MASK != 0
    }

    /// Clear the `SENDOK` interrupt by writing `1`.
    #[must_use = "clear_sendok returns a modified SocketInterrupt"]
    pub const fn clear_sendok(mut self) -> Self {
        self.0 |= Self::SENDOK_MASK;
        self
    }

    /// Returns `true` if any interrupt is raised.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterrupt;
    ///
    /// assert!(!SocketInterrupt::DEFAULT.any_raised());
    /// ```
    pub const fn any_raised(&self) -> bool {
        self.0 & Self::ALL_MASK != 0
    }
}

impl ::core::fmt::Display for SocketInterrupt {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        f.debug_struct("SocketInterrupt")
            .field("con_raised", &self.con_raised())
            .field("discon_raised", &self.discon_raised())
            .field("recv_raised", &self.recv_raised())
            .field("timeout_raised", &self.timeout_raised())
            .field("sendok_raised", &self.sendok_raised())
            .finish()
    }
}

#[cfg(feature = "defmt")]
impl defmt::Format for SocketInterrupt {
    fn format(&self, fmt: defmt::Formatter) {
        defmt::write!(
            fmt,
            "SocketInterrupt {{ con_raised: {}, discon_raised: {}, recv_raised: {}, timeout_raised: {}, sendok_raised: {} }}",
            self.con_raised(),
            self.discon_raised(),
            self.recv_raised(),
            self.timeout_raised(),
            self.sendok_raised(),
        );
    }
}

/// Socket Interrupt Mask Register (Sn_IMR).
///
/// This is used by the [`Registers::sn_imr`] and
/// [`Registers::set_sn_imr`] methods.
///
/// See the [`SocketInterrupt`] structure for more information about the
/// individual interrupts.
///
/// [`Registers::sn_imr`]: crate::Registers::sn_imr
/// [`Registers::set_sn_imr`]: crate::Registers::set_sn_imr
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SocketInterruptMask(u8);
impl_boilerplate_for!(SocketInterruptMask);

impl SocketInterruptMask {
    /// Socket interrupt mask register (Sn_IMR) reset value.
    pub const RESET: u8 = 0xFF;

    /// Default value.
    ///
    /// This is the same as `default`, but as a `const` value.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterruptMask;
    ///
    /// assert_eq!(SocketInterruptMask::DEFAULT, SocketInterruptMask::default());
    /// ```
    pub const DEFAULT: Self = Self(Self::RESET);

    /// Mask all socket interrupts.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterruptMask;
    ///
    /// assert!(SocketInterruptMask::ALL_MASKED.con_masked());
    /// assert!(SocketInterruptMask::ALL_MASKED.discon_masked());
    /// assert!(SocketInterruptMask::ALL_MASKED.recv_masked());
    /// assert!(SocketInterruptMask::ALL_MASKED.timeout_masked());
    /// assert!(SocketInterruptMask::ALL_MASKED.sendok_masked());
    /// ```
    pub const ALL_MASKED: SocketInterruptMask = SocketInterruptMask(0xE0);

    /// Check if the `CON` interrupt is masked.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterruptMask;
    ///
    /// let simr: SocketInterruptMask = SocketInterruptMask::DEFAULT;
    /// assert!(!simr.con_masked());
    /// let simr: SocketInterruptMask = simr.mask_con();
    /// assert!(simr.con_masked());
    /// let simr: SocketInterruptMask = simr.unmask_con();
    /// assert!(!simr.con_masked());
    /// ```
    pub const fn con_masked(&self) -> bool {
        self.0 & SocketInterrupt::CON_MASK == 0
    }

    /// Unmask the `CON` interrupt.
    #[must_use = "unmask_con returns a modified SocketInterruptMask"]
    pub const fn unmask_con(mut self) -> Self {
        self.0 |= SocketInterrupt::CON_MASK;
        self
    }

    /// Mask the `CON` interrupt.
    #[must_use = "mask_con returns a modified SocketInterruptMask"]
    pub const fn mask_con(mut self) -> Self {
        self.0 &= !SocketInterrupt::CON_MASK;
        self
    }

    /// Check if the `DISCON` interrupt is masked.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterruptMask;
    ///
    /// let simr: SocketInterruptMask = SocketInterruptMask::DEFAULT;
    /// assert!(!simr.discon_masked());
    /// let simr: SocketInterruptMask = simr.mask_discon();
    /// assert!(simr.discon_masked());
    /// let simr: SocketInterruptMask = simr.unmask_discon();
    /// assert!(!simr.discon_masked());
    /// ```
    pub const fn discon_masked(&self) -> bool {
        self.0 & SocketInterrupt::DISCON_MASK == 0
    }

    /// Unmask the `DISCON` interrupt.
    #[must_use = "unmask_discon returns a modified SocketInterruptMask"]
    pub const fn unmask_discon(mut self) -> Self {
        self.0 |= SocketInterrupt::DISCON_MASK;
        self
    }

    /// Mask the `DISCON` interrupt.
    #[must_use = "mask_discon returns a modified SocketInterruptMask"]
    pub const fn mask_discon(mut self) -> Self {
        self.0 &= !SocketInterrupt::DISCON_MASK;
        self
    }

    /// Check if the `RECV` interrupt is masked.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterruptMask;
    ///
    /// let simr: SocketInterruptMask = SocketInterruptMask::DEFAULT;
    /// assert!(!simr.recv_masked());
    /// let simr: SocketInterruptMask = simr.mask_recv();
    /// assert!(simr.recv_masked());
    /// let simr: SocketInterruptMask = simr.unmask_recv();
    /// assert!(!simr.recv_masked());
    /// ```
    pub const fn recv_masked(&self) -> bool {
        self.0 & SocketInterrupt::RECV_MASK == 0
    }

    /// Unmask the `RECV` interrupt.
    #[must_use = "unmask_recv returns a modified SocketInterruptMask"]
    pub const fn unmask_recv(mut self) -> Self {
        self.0 |= SocketInterrupt::RECV_MASK;
        self
    }

    /// Mask the `RECV` interrupt.
    #[must_use = "mask_recv returns a modified SocketInterruptMask"]
    pub const fn mask_recv(mut self) -> Self {
        self.0 &= !SocketInterrupt::RECV_MASK;
        self
    }

    /// Check if the `TIMEOUT` interrupt is masked.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterruptMask;
    ///
    /// let simr: SocketInterruptMask = SocketInterruptMask::DEFAULT;
    /// assert!(!simr.timeout_masked());
    /// let simr: SocketInterruptMask = simr.mask_timeout();
    /// assert!(simr.timeout_masked());
    /// let simr: SocketInterruptMask = simr.unmask_timeout();
    /// assert!(!simr.timeout_masked());
    /// ```
    pub const fn timeout_masked(&self) -> bool {
        self.0 & SocketInterrupt::TIMEOUT_MASK == 0
    }

    /// Unmask the `TIMEOUT` interrupt.
    #[must_use = "unmask_timeout returns a modified SocketInterruptMask"]
    pub const fn unmask_timeout(mut self) -> Self {
        self.0 |= SocketInterrupt::TIMEOUT_MASK;
        self
    }

    /// Mask the `TIMEOUT` interrupt.
    #[must_use = "mask_timeout returns a modified SocketInterruptMask"]
    pub const fn mask_timeout(mut self) -> Self {
        self.0 &= !SocketInterrupt::TIMEOUT_MASK;
        self
    }

    /// Check if the `SENDOK` interrupt is masked.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::SocketInterruptMask;
    ///
    /// let simr: SocketInterruptMask = SocketInterruptMask::DEFAULT;
    /// assert!(!simr.sendok_masked());
    /// let simr: SocketInterruptMask = simr.mask_sendok();
    /// assert!(simr.sendok_masked());
    /// let simr: SocketInterruptMask = simr.unmask_sendok();
    /// assert!(!simr.sendok_masked());
    /// ```
    pub const fn sendok_masked(&self) -> bool {
        self.0 & SocketInterrupt::SENDOK_MASK == 0
    }

    /// Unmask the `SENDOK` interrupt.
    #[must_use = "unmask_sendok returns a modified SocketInterruptMask"]
    pub const fn unmask_sendok(mut self) -> Self {
        self.0 |= SocketInterrupt::SENDOK_MASK;
        self
    }

    /// Mask the `SENDOK` interrupt.
    #[must_use = "mask_sendok returns a modified SocketInterruptMask"]
    pub const fn mask_sendok(mut self) -> Self {
        self.0 &= !SocketInterrupt::SENDOK_MASK;
        self
    }
}

impl ::core::fmt::Display for SocketInterruptMask {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        f.debug_struct("SocketInterruptMask")
            .field("con_masked", &self.con_masked())
            .field("discon_masked", &self.discon_masked())
            .field("recv_masked", &self.recv_masked())
            .field("timeout_masked", &self.timeout_masked())
            .field("sendok_masked", &self.sendok_masked())
            .finish()
    }
}

#[cfg(feature = "defmt")]
impl defmt::Format for SocketInterruptMask {
    fn format(&self, fmt: defmt::Formatter) {
        defmt::write!(
            fmt,
            "SocketInterruptMask {{ con_masked: {}, discon_masked: {}, recv_masked: {}, timeout_masked: {}, sendok_masked: {} }}",
            self.con_masked(),
            self.discon_masked(),
            self.recv_masked(),
            self.timeout_masked(),
            self.sendok_masked(),
        );
    }
}