w5500-ll 0.10.4

Low level driver for the Wiznet W5500 internet offload chip.
Documentation
//! Platform agnostic rust driver for the [Wiznet W5500] SPI internet offload
//! chip.
//!
//! This is a low-level (ll) crate. The scope of this crate is:
//! 1) Register accessors.
//! 2) Networking data types.
//!
//! Higher level functionality (such as socket operations) should be built
//! on-top of what is provided here.
//!
//! # Example
//!
//! Reading the VERSIONR register (a constant value).
//!
//! ```
//! # use embedded_hal_mock as hal;
//! # let spi = hal::spi::Mock::new(&[
//! #   hal::spi::Transaction::write(vec![0x00, 0x39, 0x00]),
//! #   hal::spi::Transaction::transfer(vec![0], vec![0x04]),
//! # ]);
//! # let pin = hal::pin::Mock::new(&[
//! #    hal::pin::Transaction::set(hal::pin::State::Low),
//! #    hal::pin::Transaction::set(hal::pin::State::High),
//! # ]);
//! use w5500_ll::{blocking::vdm::W5500, Registers};
//!
//! let mut w5500 = W5500::new(spi, pin);
//! let version: u8 = w5500.version()?;
//! assert_eq!(version, 0x04);
//! # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
//! ```
//!
//! # Feature Flags
//!
//! All features are disabled by default.
//!
//! * `defmt`: Enable formatting most types with `defmt`.
//! * `embedded-hal`: Enables the [`blocking`] module which contains
//!   implementations of the [`Registers`] trait using the `embedded-hal` traits.
//! * `std`: Enables conversion between [`std::net`] and [`w5500_ll::net`] types.
//!   This is for testing purposes only, the `std` flag will not work on
//!   embedded systems because it uses the standard library.
//!
//! [`std::net`]: https://doc.rust-lang.org/std/net/index.html
//! [Wiznet W5500]: https://www.wiznet.io/product-item/w5500/
//! [`blocking`]: https://docs.rs/w5500-ll/latest/w5500_ll/blocking/index.html
//! [`Registers`]: https://docs.rs/w5500-ll/latest/w5500_ll/trait.Registers.html
//! [`w5500_ll::net`]: https://docs.rs/w5500-ll/latest/w5500_ll/net/index.html
#![cfg_attr(docsrs, feature(doc_cfg), feature(doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)]
#![warn(missing_docs)]

#[cfg(feature = "embedded-hal")]
pub mod blocking;
pub mod net;
pub mod spi;

mod addr;
mod registers;
mod specifiers;
use net::{Eui48Addr, Ipv4Addr, SocketAddrV4};

pub use addr::{Reg, SnReg};
pub use registers::{Interrupt, Mode, PhyCfg, SocketInterrupt, SocketInterruptMask, SocketMode};
pub use specifiers::{
    BufferSize, DuplexStatus, LinkStatus, OperationMode, Protocol, SocketCommand, SocketStatus,
    SpeedStatus,
};

/// Common register block address offset.
pub const COMMON_BLOCK_OFFSET: u8 = 0x00;
/// Socket spacing between blocks.
const SOCKET_SPACING: u8 = 0x04;
/// Socket common block select bits offset.
const SOCKET_BLOCK_OFFSET: u8 = 0x01;
/// Socket TX block select bits offset
const SOCKET_TX_OFFSET: u8 = 0x02;
/// Socket RX block select bits offset
const SOCKET_RX_OFFSET: u8 = 0x03;

/// Value of the W5500 VERSIONR register.
///
/// This is very useful as a sanity check to ensure the W5500 is out of reset
/// and responding correctly to register reads.
///
/// # Example
///
/// ```
/// # use embedded_hal_mock as hal;
/// # let spi = hal::spi::Mock::new(&[
/// #   hal::spi::Transaction::write(vec![0x00, 0x39, 0x00]),
/// #   hal::spi::Transaction::transfer(vec![0], vec![0x04]),
/// # ]);
/// # let pin = hal::pin::Mock::new(&[
/// #    hal::pin::Transaction::set(hal::pin::State::Low),
/// #    hal::pin::Transaction::set(hal::pin::State::High),
/// # ]);
/// use w5500_ll::{blocking::vdm::W5500, Registers, VERSION};
///
/// let mut w5500 = W5500::new(spi, pin);
/// let version: u8 = w5500.version()?;
/// assert_eq!(version, VERSION);
/// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
/// ```
pub const VERSION: u8 = 0x04;

/// W5500 socket numbers.
#[repr(u8)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Sn {
    /// Socket 0.
    ///
    /// This is the only socket that can be used in the [`Macraw`] mode.
    ///
    /// [`Macraw`]: crate::Protocol::Macraw
    Sn0 = 0,
    /// Socket 1.
    Sn1 = 1,
    /// Socket 2.
    Sn2 = 2,
    /// Socket 3.
    Sn3 = 3,
    /// Socket 4.
    Sn4 = 4,
    /// Socket 5.
    Sn5 = 5,
    /// Socket 6.
    Sn6 = 6,
    /// Socket 7.
    Sn7 = 7,
}

impl Sn {
    /// Get the socket register block select bits.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Sn;
    ///
    /// assert_eq!(Sn::Sn0.block(), 0b00001);
    /// assert_eq!(Sn::Sn1.block(), 0b00101);
    /// assert_eq!(Sn::Sn2.block(), 0b01001);
    /// assert_eq!(Sn::Sn3.block(), 0b01101);
    /// assert_eq!(Sn::Sn4.block(), 0b10001);
    /// assert_eq!(Sn::Sn5.block(), 0b10101);
    /// assert_eq!(Sn::Sn6.block(), 0b11001);
    /// assert_eq!(Sn::Sn7.block(), 0b11101);
    /// ```
    #[inline]
    pub const fn block(self) -> u8 {
        SOCKET_SPACING * (self as u8) + SOCKET_BLOCK_OFFSET
    }

    /// Get the socket TX buffer block select bits.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Sn;
    ///
    /// assert_eq!(Sn::Sn0.tx_block(), 0b00010);
    /// assert_eq!(Sn::Sn1.tx_block(), 0b00110);
    /// assert_eq!(Sn::Sn2.tx_block(), 0b01010);
    /// assert_eq!(Sn::Sn3.tx_block(), 0b01110);
    /// assert_eq!(Sn::Sn4.tx_block(), 0b10010);
    /// assert_eq!(Sn::Sn5.tx_block(), 0b10110);
    /// assert_eq!(Sn::Sn6.tx_block(), 0b11010);
    /// assert_eq!(Sn::Sn7.tx_block(), 0b11110);
    /// ```
    #[inline]
    pub const fn tx_block(self) -> u8 {
        SOCKET_SPACING * (self as u8) + SOCKET_TX_OFFSET
    }

    /// Get the socket RX buffer block select bits.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::Sn;
    ///
    /// assert_eq!(Sn::Sn0.rx_block(), 0b00011);
    /// assert_eq!(Sn::Sn1.rx_block(), 0b00111);
    /// assert_eq!(Sn::Sn2.rx_block(), 0b01011);
    /// assert_eq!(Sn::Sn3.rx_block(), 0b01111);
    /// assert_eq!(Sn::Sn4.rx_block(), 0b10011);
    /// assert_eq!(Sn::Sn5.rx_block(), 0b10111);
    /// assert_eq!(Sn::Sn6.rx_block(), 0b11011);
    /// assert_eq!(Sn::Sn7.rx_block(), 0b11111);
    /// ```
    #[inline]
    pub const fn rx_block(self) -> u8 {
        SOCKET_SPACING * (self as u8) + SOCKET_RX_OFFSET
    }

    /// Socket bitmask.
    ///
    /// This is useful for masking socket interrupts with [`set_simr`].
    ///
    /// # Examples
    ///
    /// Demonstration:
    ///
    /// ```
    /// use w5500_ll::Sn;
    ///
    /// assert_eq!(Sn::Sn0.bitmask(), 0x01);
    /// assert_eq!(Sn::Sn1.bitmask(), 0x02);
    /// assert_eq!(Sn::Sn2.bitmask(), 0x04);
    /// assert_eq!(Sn::Sn3.bitmask(), 0x08);
    /// assert_eq!(Sn::Sn4.bitmask(), 0x10);
    /// assert_eq!(Sn::Sn5.bitmask(), 0x20);
    /// assert_eq!(Sn::Sn6.bitmask(), 0x40);
    /// assert_eq!(Sn::Sn7.bitmask(), 0x80);
    /// ```
    ///
    /// As an argument of [`set_simr`]:
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x18, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x0A]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{
    ///     blocking::vdm::W5500,
    ///     Registers,
    ///     Sn::{Sn1, Sn3},
    /// };
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// // enable socket 1 and socket 3 interrupts
    /// const SOCKET_INTERRUPT_MASK: u8 = Sn1.bitmask() | Sn3.bitmask();
    /// w5500.set_simr(SOCKET_INTERRUPT_MASK)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    ///
    /// [`set_simr`]: crate::Registers::set_simr
    pub const fn bitmask(self) -> u8 {
        1 << (self as u8)
    }

    /// Iterate over all sockets.
    ///
    /// # Example
    ///
    /// Check all sockets for a pending interrupt.
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x17, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketCommand};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    ///
    /// let sir: u8 = w5500.sir()?;
    /// for sn in Sn::iter() {
    ///     let mask: u8 = sn.bitmask();
    ///     if sir & sn.bitmask() != 0 {
    ///         // handle socket interrupt
    ///     }
    /// }
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    #[inline]
    pub fn iter() -> core::slice::Iter<'static, Self> {
        SOCKETS.iter()
    }
}

impl From<Sn> for u8 {
    #[inline]
    fn from(s: Sn) -> Self {
        s as u8
    }
}

impl From<Sn> for usize {
    #[inline]
    fn from(s: Sn) -> Self {
        usize::from(u8::from(s))
    }
}

impl TryFrom<u8> for Sn {
    type Error = u8;

    fn try_from(val: u8) -> Result<Sn, u8> {
        match val {
            0 => Ok(Sn::Sn0),
            1 => Ok(Sn::Sn1),
            2 => Ok(Sn::Sn2),
            3 => Ok(Sn::Sn3),
            4 => Ok(Sn::Sn4),
            5 => Ok(Sn::Sn5),
            6 => Ok(Sn::Sn6),
            7 => Ok(Sn::Sn7),
            x => Err(x),
        }
    }
}

/// Array of all sockets.
///
/// Useful for iterating over sockets.
///
/// # Example
///
/// Close all sockets.
///
/// ```
/// # use w5500_ll::Sn::*;
/// # use embedded_hal_mock as hal;
/// # let spi = hal::spi::Mock::new(&[
/// #   hal::spi::Transaction::write(vec![0x00, 0x01, (Sn0.block() << 3) | 0x04]),
/// #   hal::spi::Transaction::write(vec![SocketCommand::Close.into()]),
/// #   hal::spi::Transaction::write(vec![0x00, 0x01, (Sn1.block() << 3) | 0x04]),
/// #   hal::spi::Transaction::write(vec![SocketCommand::Close.into()]),
/// #   hal::spi::Transaction::write(vec![0x00, 0x01, (Sn2.block() << 3) | 0x04]),
/// #   hal::spi::Transaction::write(vec![SocketCommand::Close.into()]),
/// #   hal::spi::Transaction::write(vec![0x00, 0x01, (Sn3.block() << 3) | 0x04]),
/// #   hal::spi::Transaction::write(vec![SocketCommand::Close.into()]),
/// #   hal::spi::Transaction::write(vec![0x00, 0x01, (Sn4.block() << 3) | 0x04]),
/// #   hal::spi::Transaction::write(vec![SocketCommand::Close.into()]),
/// #   hal::spi::Transaction::write(vec![0x00, 0x01, (Sn5.block() << 3) | 0x04]),
/// #   hal::spi::Transaction::write(vec![SocketCommand::Close.into()]),
/// #   hal::spi::Transaction::write(vec![0x00, 0x01, (Sn6.block() << 3) | 0x04]),
/// #   hal::spi::Transaction::write(vec![SocketCommand::Close.into()]),
/// #   hal::spi::Transaction::write(vec![0x00, 0x01, (Sn7.block() << 3) | 0x04]),
/// #   hal::spi::Transaction::write(vec![SocketCommand::Close.into()]),
/// # ]);
/// # let pin = hal::pin::Mock::new(&[
/// #    hal::pin::Transaction::set(hal::pin::State::Low),
/// #    hal::pin::Transaction::set(hal::pin::State::High),
/// #    hal::pin::Transaction::set(hal::pin::State::Low),
/// #    hal::pin::Transaction::set(hal::pin::State::High),
/// #    hal::pin::Transaction::set(hal::pin::State::Low),
/// #    hal::pin::Transaction::set(hal::pin::State::High),
/// #    hal::pin::Transaction::set(hal::pin::State::Low),
/// #    hal::pin::Transaction::set(hal::pin::State::High),
/// #    hal::pin::Transaction::set(hal::pin::State::Low),
/// #    hal::pin::Transaction::set(hal::pin::State::High),
/// #    hal::pin::Transaction::set(hal::pin::State::Low),
/// #    hal::pin::Transaction::set(hal::pin::State::High),
/// #    hal::pin::Transaction::set(hal::pin::State::Low),
/// #    hal::pin::Transaction::set(hal::pin::State::High),
/// #    hal::pin::Transaction::set(hal::pin::State::Low),
/// #    hal::pin::Transaction::set(hal::pin::State::High),
/// # ]);
/// use w5500_ll::{blocking::vdm::W5500, Registers, SocketCommand, SOCKETS};
///
/// let mut w5500 = W5500::new(spi, pin);
/// for socket in SOCKETS.iter() {
///     w5500.set_sn_cr(*socket, SocketCommand::Close)?;
/// }
/// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
/// ```
pub const SOCKETS: [Sn; 8] = [
    Sn::Sn0,
    Sn::Sn1,
    Sn::Sn2,
    Sn::Sn3,
    Sn::Sn4,
    Sn::Sn5,
    Sn::Sn6,
    Sn::Sn7,
];

/// Reset the W5500 using the reset pin.
///
/// This function performs the following sequence:
///
/// 1. Set the reset pin low.
/// 2. Wait 1 ms (2x longer than the minimum reset cycle time of 500 µs).
/// 3. Set the reset pin high.
/// 4. Wait 2 ms (2x longer than the maximum PLL lock time of 1 ms).
///
/// # Example
///
/// ```
/// # use embedded_hal_mock as hal;
/// # let mut delay = hal::delay::MockNoop::new();
/// # let mut reset_pin = hal::pin::Mock::new(&[
/// #    hal::pin::Transaction::set(hal::pin::State::Low),
/// #    hal::pin::Transaction::set(hal::pin::State::High),
/// # ]);
/// w5500_ll::reset(&mut reset_pin, &mut delay)?;
/// # Ok::<(), hal::MockError>(())
/// ```
#[cfg(feature = "embedded-hal")]
pub fn reset<P, D, E>(pin: &mut P, delay: &mut D) -> Result<(), E>
where
    P: embedded_hal::digital::v2::OutputPin<Error = E>,
    D: embedded_hal::blocking::delay::DelayMs<u8>,
{
    pin.set_low()?;
    delay.delay_ms(1);
    pin.set_high()?;
    delay.delay_ms(2);
    Ok(())
}

/// W5500 register setters and getters.
///
/// * All register getters are simply the name of the register.
/// * All register setters are the name of the register prefixed with `set_`.
///
/// Most of the register documentation is taken from the data sheet.
pub trait Registers {
    /// Register accessor error type.
    type Error;

    /// Read from the W5500.
    ///
    /// # Arguments
    ///
    /// * `addr` - Starting address of the memory being read.
    /// * `block` - W5500 block select bits
    /// * `data` - Buffer to read data into. The number of bytes read is equal
    ///   to the length of this buffer.
    fn read(&mut self, addr: u16, block: u8, data: &mut [u8]) -> Result<(), Self::Error>;

    /// Write to the W5500.
    ///
    /// # Arguments
    ///
    /// * `addr` - Starting address of the memory being written.
    /// * `block` - W5500 block select bits
    /// * `data` - Buffer of data to write. The number of bytes written is equal
    ///   to the length of this buffer.
    fn write(&mut self, addr: u16, block: u8, data: &[u8]) -> Result<(), Self::Error>;

    /// Get the mode register.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x00, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Mode, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let mode: Mode = w5500.mr()?;
    /// assert_eq!(mode, Mode::default());
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn mr(&mut self) -> Result<Mode, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(Reg::MR.addr(), COMMON_BLOCK_OFFSET, &mut reg)?;
        Ok(Mode::from(reg[0]))
    }

    /// Set the mode register.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x00, 0x04]),
    /// #   hal::spi::Transaction::write(vec![w5500_ll::Mode::WOL_MASK]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Mode, Registers};
    ///
    /// const MODE: Mode = Mode::DEFAULT.enable_wol();
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_mr(MODE)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_mr(&mut self, mode: Mode) -> Result<(), Self::Error> {
        self.write(Reg::MR.addr(), COMMON_BLOCK_OFFSET, &[mode.into()])
    }

    /// Get the gateway IP address.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x01, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0, 0, 0], vec![0, 0, 0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Ipv4Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let gar = w5500.gar()?;
    /// assert_eq!(gar, Ipv4Addr::UNSPECIFIED);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn gar(&mut self) -> Result<Ipv4Addr, Self::Error> {
        let mut gar = Ipv4Addr::UNSPECIFIED;
        self.read(Reg::GAR0.addr(), COMMON_BLOCK_OFFSET, &mut gar.octets)?;
        Ok(gar)
    }

    /// Set the gateway IP address.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x01, 0x04]),
    /// #   hal::spi::Transaction::write(vec![192, 168, 0, 1]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Ipv4Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_gar(&Ipv4Addr::new(192, 168, 0, 1))?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_gar(&mut self, gar: &Ipv4Addr) -> Result<(), Self::Error> {
        self.write(Reg::GAR0.addr(), COMMON_BLOCK_OFFSET, &gar.octets)
    }

    /// Get the subnet mask.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x05, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0, 0, 0], vec![0, 0, 0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Ipv4Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let subr = w5500.subr()?;
    /// assert_eq!(subr, Ipv4Addr::UNSPECIFIED);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn subr(&mut self) -> Result<Ipv4Addr, Self::Error> {
        let mut subr = Ipv4Addr::UNSPECIFIED;
        self.read(Reg::SUBR0.addr(), COMMON_BLOCK_OFFSET, &mut subr.octets)?;
        Ok(subr)
    }

    /// Set the subnet mask.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x05, 0x04]),
    /// #   hal::spi::Transaction::write(vec![255, 255, 255, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Ipv4Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_subr(&Ipv4Addr::new(255, 255, 255, 0))?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_subr(&mut self, subr: &Ipv4Addr) -> Result<(), Self::Error> {
        self.write(Reg::SUBR0.addr(), COMMON_BLOCK_OFFSET, &subr.octets)
    }

    /// Get the source hardware address.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x09, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0, 0, 0, 0, 0], vec![0, 0, 0, 0, 0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Eui48Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let shar = w5500.shar()?;
    /// assert_eq!(shar, Eui48Addr::UNSPECIFIED);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn shar(&mut self) -> Result<Eui48Addr, Self::Error> {
        let mut shar = Eui48Addr::UNSPECIFIED;
        self.read(Reg::SHAR0.addr(), COMMON_BLOCK_OFFSET, &mut shar.octets)?;
        Ok(shar)
    }

    /// Set the source hardware address.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x09, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x12, 0x34, 0, 0, 0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Eui48Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_shar(&Eui48Addr::new(0x12, 0x34, 0x00, 0x00, 0x00, 0x00))?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_shar(&mut self, shar: &Eui48Addr) -> Result<(), Self::Error> {
        self.write(Reg::SHAR0.addr(), COMMON_BLOCK_OFFSET, &shar.octets)
    }

    /// Get the source (client) IP address.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x0F, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0, 0, 0], vec![0, 0, 0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Ipv4Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sipr = w5500.sipr()?;
    /// assert_eq!(sipr, Ipv4Addr::UNSPECIFIED);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sipr(&mut self) -> Result<Ipv4Addr, Self::Error> {
        let mut sipr = Ipv4Addr::UNSPECIFIED;
        self.read(Reg::SIPR0.addr(), COMMON_BLOCK_OFFSET, &mut sipr.octets)?;
        Ok(sipr)
    }

    /// Set the source (client) IP address.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x0F, 0x04]),
    /// #   hal::spi::Transaction::write(vec![192, 168, 0, 150]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Ipv4Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sipr(&Ipv4Addr::new(192, 168, 0, 150))?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sipr(&mut self, sipr: &Ipv4Addr) -> Result<(), Self::Error> {
        self.write(Reg::SIPR0.addr(), COMMON_BLOCK_OFFSET, &sipr.octets)
    }

    /// Get the interrupt low level time.
    ///
    /// INTLEVEL configures the assert wait time (I<sub>AWT</sub>).
    ///
    /// When the  next interrupt occurs, the interrupt in (INTn) will assert
    /// to low after INTLEVEL time.
    ///
    /// The equation is:
    ///
    /// I<sub>AWT</sub> = (INTLEVEL + 1) * PLL<sub>CLK</sub> * 4
    ///
    /// When INTLEVEL > 0.
    ///
    /// You might want to take a look at the data sheet, there is a handy
    /// timing diagram there.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x13, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let intlevel: u16 = w5500.intlevel()?;
    /// assert_eq!(intlevel, 0x00);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn intlevel(&mut self) -> Result<u16, Self::Error> {
        let mut buf: [u8; 2] = [0; 2];
        self.read(Reg::INTLEVEL0.addr(), COMMON_BLOCK_OFFSET, &mut buf)?;
        Ok(u16::from_be_bytes(buf))
    }

    /// Set the interrupt low level time.
    ///
    /// See [`Registers::intlevel`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x13, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x12, 0x34]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_intlevel(0x1234)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_intlevel(&mut self, intlevel: u16) -> Result<(), Self::Error> {
        self.write(
            Reg::INTLEVEL0.addr(),
            COMMON_BLOCK_OFFSET,
            &intlevel.to_be_bytes(),
        )
    }

    /// Get the interrupt status.
    ///
    /// `1` indicates the interrupt is raised.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x15, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Interrupt, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let ir: Interrupt = w5500.ir()?;
    /// assert_eq!(ir, Interrupt::default());
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn ir(&mut self) -> Result<Interrupt, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(Reg::IR.addr(), COMMON_BLOCK_OFFSET, &mut reg)?;
        Ok(Interrupt::from(reg[0]))
    }

    /// Set the interrupt status.
    ///
    /// Setting an interrupt bit to `1` will clear the interrupt.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x15, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// #   hal::spi::Transaction::write(vec![0x00, 0x15, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Interrupt, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let ir: Interrupt = w5500.ir()?;
    /// w5500.set_ir(ir)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_ir(&mut self, interrupt: Interrupt) -> Result<(), Self::Error> {
        self.write(Reg::IR.addr(), COMMON_BLOCK_OFFSET, &[interrupt.into()])
    }

    /// Get the interrupt mask.
    ///
    /// `0` indicates the interrupt is masked.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x16, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Interrupt, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let imr: Interrupt = w5500.imr()?;
    /// assert_eq!(imr, Interrupt::default());
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn imr(&mut self) -> Result<Interrupt, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(Reg::IMR.addr(), COMMON_BLOCK_OFFSET, &mut reg)?;
        Ok(Interrupt::from(reg[0]))
    }

    /// Set the interrupt mask.
    ///
    /// Setting an interrupt bit to `1` will mask the interrupt.
    /// When a bit of IMR is `1` and the corresponding interrupt is `1` an
    /// interrupt will be issued.
    /// If a bit of IMR is `0`, and interrupt will not be issued even if the
    /// corresponding IR bit is `1`.
    ///
    /// # Example
    ///
    /// ```
    /// use w5500_ll::{blocking::vdm::W5500, Interrupt, Registers};
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x16, 0x04]),
    /// #   hal::spi::Transaction::write(vec![Interrupt::MP_MASK]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    ///
    /// // enable the magic packet interrupt
    /// const IMR: Interrupt = Interrupt::DEFAULT.set_mp();
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_imr(IMR)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_imr(&mut self, mask: Interrupt) -> Result<(), Self::Error> {
        self.write(Reg::IMR.addr(), COMMON_BLOCK_OFFSET, &[mask.into()])
    }

    /// Get the socket interrupt status.
    ///
    /// SIMR indicates the interrupt status of all sockets.
    /// Each bit of SIR will be `1` until [`sn_ir`] is cleared.
    /// If [`sn_ir`] is not equal to `0x00` the n<sub>th</sub>
    /// bit of `sir` is `1` and the INTn pin is asserted until SIR is `0x00`.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x17, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, SOCKETS};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sir = w5500.sir()?;
    /// // clear all socket interrupts
    /// for socket in SOCKETS.iter() {
    ///     if 1 << (*socket as u8) & sir != 0 {
    ///         let sn_ir = w5500.sn_ir(*socket)?;
    ///         w5500.set_sn_ir(*socket, sn_ir)?;
    ///     }
    /// }
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    ///
    /// [`sn_ir`]: Registers::sn_ir
    fn sir(&mut self) -> Result<u8, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(Reg::SIR.addr(), COMMON_BLOCK_OFFSET, &mut reg)?;
        Ok(reg[0])
    }

    /// Get the socket interrupt mask.
    ///
    /// Each bit of SIMR corresponds to each bit of [`Registers::sir`].
    /// When a bit of SIMR is `1` and the corresponding bit of SIR is `1`
    /// and interrupt will be issued.
    /// If a bit of SIMR is `0` an interrupt will be not issued even if the
    /// corresponding bit of SIR is `1`.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x18, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let simr: u8 = w5500.simr()?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn simr(&mut self) -> Result<u8, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(Reg::SIMR.addr(), COMMON_BLOCK_OFFSET, &mut reg)?;
        Ok(reg[0])
    }

    /// Set the socket interrupt mask.
    ///
    /// See [`Registers::simr`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x18, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0xFF]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// // enable all socket interrupts
    /// w5500.set_simr(0xFF)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_simr(&mut self, simr: u8) -> Result<(), Self::Error> {
        self.write(Reg::SIMR.addr(), COMMON_BLOCK_OFFSET, &[simr])
    }

    /// Get the retry time.
    ///
    /// RTR configures the re-transmission timeout period.
    /// The unit of timeout period is 100 us and the default of RTR is `0x07D0`
    /// or `2000`.
    /// And so the default timeout period is 200 ms (100 us X 2000).
    /// During the time configured by RTR, the W5500 waits for the peer response
    /// to the packet that is transmitted by Sn_CR (CONNECT, DISCON, CLOSE,
    /// SEND, SEND_MAC, SEND_KEEP command).
    /// If the peer does not respond within the RTR time, the W5500 re-transmits
    /// the packet or issues a timeout.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x19, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x07, 0xD0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let rtr: u16 = w5500.rtr()?;
    /// assert_eq!(rtr, 0x07D0);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn rtr(&mut self) -> Result<u16, Self::Error> {
        let mut buf: [u8; 2] = [0; 2];
        self.read(Reg::RTR0.addr(), COMMON_BLOCK_OFFSET, &mut buf)?;
        Ok(u16::from_be_bytes(buf))
    }

    /// Set the retry time.
    ///
    /// See [`Registers::rtr`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x19, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x12, 0x34]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_rtr(0x1234)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_rtr(&mut self, rtr: u16) -> Result<(), Self::Error> {
        self.write(Reg::RTR0.addr(), COMMON_BLOCK_OFFSET, &rtr.to_be_bytes())
    }

    /// Get the retry count.
    ///
    /// RCR configured the number of re-transmission attempts.
    /// When the number of re-transmission equals RCR + 1 the socket timeout
    /// interrupt is raised.
    ///
    /// There is a LOT more information in the data sheet,
    /// including some equations that would be very annoying to input.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1B, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0x08]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let rcr: u8 = w5500.rcr()?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn rcr(&mut self) -> Result<u8, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(Reg::RCR.addr(), COMMON_BLOCK_OFFSET, &mut reg)?;
        Ok(reg[0])
    }

    /// Set the retry count.
    ///
    /// See [`Registers::rcr`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1B, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x0A]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_rcr(0x0A)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_rcr(&mut self, rcr: u8) -> Result<(), Self::Error> {
        self.write(Reg::RCR.addr(), COMMON_BLOCK_OFFSET, &[rcr])
    }

    /// Get the PPP link control protocol request timer.
    ///
    /// PTIMER configures the time for sending LCP echo request.
    ///
    /// The unit of time is 25 milliseconds, for a register value of 200 the
    /// timer is 5 seconds.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1C, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0x08]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let ptimer: u8 = w5500.ptimer()?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn ptimer(&mut self) -> Result<u8, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(Reg::PTIMER.addr(), COMMON_BLOCK_OFFSET, &mut reg)?;
        Ok(reg[0])
    }

    /// Set the PPP link control protocol request timer.
    ///
    /// See [`Registers::ptimer`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1C, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0xC8]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_ptimer(200)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_ptimer(&mut self, ptimer: u8) -> Result<(), Self::Error> {
        self.write(Reg::PTIMER.addr(), COMMON_BLOCK_OFFSET, &[ptimer])
    }

    /// Get the PPP link control protocol magic number.
    ///
    /// PMAGIC configures the 4 byte magic number used in the LCP echo request.
    /// For a register value of `0x01` the magic number is `0x01010101`.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1D, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0x08]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let pmagic: u8 = w5500.pmagic()?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn pmagic(&mut self) -> Result<u8, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(Reg::PMAGIC.addr(), COMMON_BLOCK_OFFSET, &mut reg)?;
        Ok(reg[0])
    }

    /// Set the PPP link control protocol magic number.
    ///
    /// See [`Registers::pmagic`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1D, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x01]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_pmagic(0x01)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_pmagic(&mut self, pmagic: u8) -> Result<(), Self::Error> {
        self.write(Reg::PMAGIC.addr(), COMMON_BLOCK_OFFSET, &[pmagic])
    }

    /// Get the destination hardware address in PPPoE mode.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1E, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0, 0, 0, 0, 0], vec![0, 0, 0, 0, 0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Eui48Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let phar = w5500.phar()?;
    /// assert_eq!(phar, Eui48Addr::UNSPECIFIED);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn phar(&mut self) -> Result<Eui48Addr, Self::Error> {
        let mut phar = Eui48Addr::UNSPECIFIED;
        self.read(Reg::PHAR0.addr(), COMMON_BLOCK_OFFSET, &mut phar.octets)?;
        Ok(phar)
    }

    /// Set the destination hardware address in PPPoE mode.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1E, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x12, 0x34, 0, 0, 0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Eui48Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_phar(&Eui48Addr::new(0x12, 0x34, 0x00, 0x00, 0x00, 0x00))?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_phar(&mut self, phar: &Eui48Addr) -> Result<(), Self::Error> {
        self.write(Reg::PHAR0.addr(), COMMON_BLOCK_OFFSET, &phar.octets)
    }

    /// Get the session ID in PPPoE mode.
    ///
    /// PSID should be written to the PPPoE server session ID acquired in the
    /// PPPoE connection process.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x24, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let psid: u16 = w5500.psid()?;
    /// assert_eq!(psid, 0x0000);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn psid(&mut self) -> Result<u16, Self::Error> {
        let mut buf: [u8; 2] = [0; 2];
        self.read(Reg::PSID0.addr(), COMMON_BLOCK_OFFSET, &mut buf)?;
        Ok(u16::from_be_bytes(buf))
    }

    /// Set the session ID in PPPoE mode.
    ///
    /// See [`Registers::psid`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x24, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x12, 0x34]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_psid(0x1234)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_psid(&mut self, psid: u16) -> Result<(), Self::Error> {
        self.write(Reg::PSID0.addr(), COMMON_BLOCK_OFFSET, &psid.to_be_bytes())
    }

    /// Get the maximum receive unit in PPPoE mode.
    ///
    /// PMRU configures the maximum receive unit of PPPoE.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x26, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let pmru: u16 = w5500.pmru()?;
    /// assert_eq!(pmru, 0x0000);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn pmru(&mut self) -> Result<u16, Self::Error> {
        let mut buf: [u8; 2] = [0; 2];
        self.read(Reg::PMRU0.addr(), COMMON_BLOCK_OFFSET, &mut buf)?;
        Ok(u16::from_be_bytes(buf))
    }

    /// Set the maximum receive unit in PPPoE mode.
    ///
    /// See [`Registers::pmru`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x26, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x12, 0x34]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_pmru(0x1234)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_pmru(&mut self, pmru: u16) -> Result<(), Self::Error> {
        self.write(Reg::PMRU0.addr(), COMMON_BLOCK_OFFSET, &pmru.to_be_bytes())
    }

    /// Get the unreachable IP address.
    ///
    /// This awkward wording is taken directly from the data-sheet:
    ///
    /// W5500 receives an ICMP packet (destination port unreachable)
    /// when data is sent to a port number which socket is not open and
    /// the UNREACH bit of [`Registers::ir`] becomes `1` and UIPR and UPORTR
    /// indicates the destination IP address and port number respectively.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x28, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0, 0, 0], vec![0, 0, 0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Ipv4Addr, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let uipr = w5500.uipr()?;
    /// assert_eq!(uipr, Ipv4Addr::UNSPECIFIED);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn uipr(&mut self) -> Result<Ipv4Addr, Self::Error> {
        let mut uipr = Ipv4Addr::UNSPECIFIED;
        self.read(Reg::UIPR0.addr(), COMMON_BLOCK_OFFSET, &mut uipr.octets)?;
        Ok(uipr)
    }

    /// Get the unreachable port.
    ///
    /// See [`Registers::uipr`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x2C, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let uportr = w5500.uportr()?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn uportr(&mut self) -> Result<u16, Self::Error> {
        let mut buf: [u8; 2] = [0; 2];
        self.read(Reg::UPORTR0.addr(), COMMON_BLOCK_OFFSET, &mut buf)?;
        Ok(u16::from_be_bytes(buf))
    }

    /// Get the PHY configuration.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x2E, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0b10111000]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, PhyCfg, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let phy_cfg: PhyCfg = w5500.phycfgr()?;
    /// assert_eq!(phy_cfg, PhyCfg::default());
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn phycfgr(&mut self) -> Result<PhyCfg, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(Reg::PHYCFGR.addr(), COMMON_BLOCK_OFFSET, &mut reg)?;
        Ok(PhyCfg::from(reg[0]))
    }

    /// Set the PHY configuration.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x2E, 0x04]),
    /// #   hal::spi::Transaction::write(vec![0b11111000]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, OperationMode, PhyCfg, Registers};
    ///
    /// const PHY_CFG: PhyCfg = PhyCfg::DEFAULT.set_opmdc(OperationMode::Auto);
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_phycfgr(PHY_CFG)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_phycfgr(&mut self, phycfg: PhyCfg) -> Result<(), Self::Error> {
        self.write(Reg::PHYCFGR.addr(), COMMON_BLOCK_OFFSET, &[phycfg.into()])
    }

    /// Get the version.
    ///
    /// The value returned is always `0x04`.
    ///
    /// This register is extremely useful as a sanity check to ensure SPI
    /// communications are working with the W5500.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x39, 0x00]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0x04]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let version = w5500.version()?;
    /// assert_eq!(version, 0x04);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn version(&mut self) -> Result<u8, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(Reg::VERSIONR.addr(), COMMON_BLOCK_OFFSET, &mut reg)?;
        Ok(reg[0])
    }

    /// Get the socket mode.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x00, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0x00]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketMode};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let socket_mode = w5500.sn_mr(Sn::Sn0)?;
    /// assert_eq!(socket_mode, SocketMode::default());
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_mr(&mut self, sn: Sn) -> Result<SocketMode, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(SnReg::MR.addr(), sn.block(), &mut reg)?;
        Ok(SocketMode::from(reg[0]))
    }

    /// Set the socket mode.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x00, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x01]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Protocol, Registers, Sn, SocketMode};
    ///
    /// const SOCKET_MODE: SocketMode = SocketMode::DEFAULT.set_protocol(Protocol::Tcp);
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_mr(Sn::Sn0, SOCKET_MODE)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_mr(&mut self, sn: Sn, mode: SocketMode) -> Result<(), Self::Error> {
        self.write(SnReg::MR.addr(), sn.block(), &[mode.into()])
    }

    /// Get the socket command.
    ///
    /// The only use for reading this register is to check if a socket command
    /// has been accepted.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x01, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x01]),
    /// #   hal::spi::Transaction::write(vec![0x00, 0x01, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![1]),
    /// #   hal::spi::Transaction::write(vec![0x00, 0x01, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketCommand};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_cr(Sn::Sn0, SocketCommand::Open)?;
    /// loop {
    ///     if w5500.sn_cr(Sn::Sn0)? == SocketCommand::Accepted.into() {
    ///         break;
    ///     }
    /// }
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_cr(&mut self, sn: Sn) -> Result<u8, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(SnReg::CR.addr(), sn.block(), &mut reg)?;
        Ok(reg[0])
    }

    /// Set the socket command.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x01, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x01]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketCommand};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_cr(Sn::Sn0, SocketCommand::Open)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_cr(&mut self, sn: Sn, cmd: SocketCommand) -> Result<(), Self::Error> {
        self.write(SnReg::CR.addr(), sn.block(), &[cmd.into()])
    }

    /// Get the socket interrupt status.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x02, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let socket_interrupts = w5500.sn_ir(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_ir(&mut self, sn: Sn) -> Result<SocketInterrupt, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(SnReg::IR.addr(), sn.block(), &mut reg)?;
        Ok(SocketInterrupt::from(reg[0]))
    }

    /// Set the socket interrupt status.
    ///
    /// This is a write 1 to clear register.
    ///
    /// # Examples
    ///
    /// Clearing all raised interrupts.
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x02, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// #   hal::spi::Transaction::write(vec![0x00, 0x02, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketInterrupt};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let socket_interrupts: SocketInterrupt = w5500.sn_ir(Sn::Sn0)?;
    /// w5500.set_sn_ir(Sn::Sn0, socket_interrupts)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    ///
    /// Clearing only the SENDOK interrupt.
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x02, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![SocketInterrupt::SENDOK_MASK]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketInterrupt};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_ir(Sn::Sn0, SocketInterrupt::SENDOK_MASK)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_ir<T: Into<u8>>(&mut self, sn: Sn, sn_ir: T) -> Result<(), Self::Error> {
        self.write(SnReg::IR.addr(), sn.block(), &[sn_ir.into()])
    }

    /// Get the socket status.
    ///
    /// **Note:** This method returns a nested [`core::result::Result`].
    ///
    /// The outermost `Result` is for handling bus errors, similar to most of
    /// the other methods in this trait.
    ///
    /// The innermost `Result<SocketStatus, u8>` is the result of a `u8` to
    /// [`SocketStatus`] conversion because not every value of `u8` corresponds
    /// to a valid [`SocketStatus`].
    /// * `u8` values that have a corresponding [`SocketStatus`] will be
    ///   converted and returned in the [`Ok`] variant of the inner `Result`.
    /// * `u8` values that do not corresponding [`SocketStatus`] will have the
    ///   raw `u8` byte returned in the [`Err`] variant of the inner `Result`.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x03, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketStatus};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_sr = w5500.sn_sr(Sn::Sn0)?;
    /// assert_eq!(sn_sr, Ok(SocketStatus::Closed));
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    ///
    /// [`Ok`]: https://doc.rust-lang.org/core/result/enum.Result.html#variant.Ok
    /// [`Err`]: https://doc.rust-lang.org/core/result/enum.Result.html#variant.Err
    fn sn_sr(&mut self, sn: Sn) -> Result<Result<SocketStatus, u8>, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(SnReg::SR.addr(), sn.block(), &mut reg)?;
        Ok(SocketStatus::try_from(reg[0]))
    }

    /// Get the socket source port.
    ///
    /// This is only valid in TCP/UDP mode.
    /// This should be set before sending the OPEN command.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x04, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketMode};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let socket_port: u16 = w5500.sn_port(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_port(&mut self, sn: Sn) -> Result<u16, Self::Error> {
        let mut reg: [u8; 2] = [0; 2];
        self.read(SnReg::PORT0.addr(), sn.block(), &mut reg)?;
        Ok(u16::from_be_bytes(reg))
    }

    /// Set the socket source port.
    ///
    /// See [`Registers::sn_port`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x04, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x00, 68]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_port(Sn::Sn0, 68)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_port(&mut self, sn: Sn, port: u16) -> Result<(), Self::Error> {
        self.write(SnReg::PORT0.addr(), sn.block(), &u16::to_be_bytes(port))
    }

    /// Get the socket destination hardware address.
    ///
    /// Sn_DHAR configures the destination hardware address of Socket n when
    /// using SEND_MAC command in UDP mode or it indicates that it is acquired
    /// in ARP-process by CONNECT/SEND command.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x06, 0x08]),
    /// #   hal::spi::Transaction::transfer(
    /// #       vec![0, 0, 0, 0, 0, 0],
    /// #       vec![0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
    /// #   ),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let dhar = w5500.sn_dhar(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_dhar(&mut self, sn: Sn) -> Result<Eui48Addr, Self::Error> {
        let mut dhar: Eui48Addr = Eui48Addr::UNSPECIFIED;
        self.read(SnReg::DHAR0.addr(), sn.block(), &mut dhar.octets)?;
        Ok(dhar)
    }

    /// Set the socket destination hardware address.
    ///
    /// See [`Registers::sn_dhar`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x06, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(
    /// #       vec![0x12, 0x34, 0x00, 0x00, 0x00, 0x00]
    /// #   ),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Eui48Addr, Registers, Sn};
    ///
    /// let dhar = Eui48Addr::new(0x12, 0x34, 0x00, 0x00, 0x00, 0x00);
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_dhar(Sn::Sn0, &dhar)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_dhar(&mut self, sn: Sn, dhar: &Eui48Addr) -> Result<(), Self::Error> {
        self.write(SnReg::DHAR0.addr(), sn.block(), &dhar.octets)
    }

    /// Get the socket destination IP address.
    ///
    /// This register configures or indicates the destination IP address.
    /// It it valid when the socket is in TCP/UDP mode.
    ///
    /// In TCP client mode it configures the TCP server address before the
    /// [`SocketCommand::Connect`] command.
    ///
    /// In TCP server mode it indicates the IP address of the TCP client after
    /// successfully establishing a connection.
    ///
    /// In UDP mode it configures an IP address of the peer to receive the UDP
    /// packet send by the [`SocketCommand::Send`] or [`SocketCommand::SendMac`]
    /// command.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x0C, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0, 0, 0], vec![0, 0, 0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Ipv4Addr, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let dipr = w5500.sn_dipr(Sn::Sn0)?;
    /// assert_eq!(dipr, Ipv4Addr::UNSPECIFIED);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_dipr(&mut self, sn: Sn) -> Result<Ipv4Addr, Self::Error> {
        let mut dipr: Ipv4Addr = Ipv4Addr::UNSPECIFIED;
        self.read(SnReg::DIPR0.addr(), sn.block(), &mut dipr.octets)?;
        Ok(dipr)
    }

    /// Set the socket destination IP address.
    ///
    /// See [`Registers::sn_dipr`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x0C, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![192, 168, 0, 11]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::Ipv4Addr, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_dipr(Sn::Sn0, &Ipv4Addr::new(192, 168, 0, 11))?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_dipr(&mut self, sn: Sn, dipr: &Ipv4Addr) -> Result<(), Self::Error> {
        self.write(SnReg::DIPR0.addr(), sn.block(), &dipr.octets)
    }

    /// Get the socket destination port.
    ///
    /// This register configures or indicates the destination port number of
    /// the socket.
    /// It is valid when the socket is used in TCP/UDP mode.
    ///
    /// In TCP client mode, it configures the listen port number of the TCP
    /// server before the [`SocketCommand::Send`] command.
    ///
    /// In TCP server mode, it indicates the port number of the TCP client
    /// after successfully establishing connection.
    ///
    /// In UDP mode, it configures the port number of the peer to be transmitted
    /// in the UDP packet by the [`SocketCommand::Send`] or
    /// [`SocketCommand::SendMac`] command.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x10, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketMode};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let socket_destination_port: u16 = w5500.sn_dport(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_dport(&mut self, sn: Sn) -> Result<u16, Self::Error> {
        let mut reg: [u8; 2] = [0; 2];
        self.read(SnReg::DPORT0.addr(), sn.block(), &mut reg)?;
        Ok(u16::from_be_bytes(reg))
    }

    /// Set the socket destination port.
    ///
    /// See [`Registers::sn_dport`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x10, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x00, 67]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_dport(Sn::Sn0, 67)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_dport(&mut self, sn: Sn, port: u16) -> Result<(), Self::Error> {
        self.write(SnReg::DPORT0.addr(), sn.block(), &u16::to_be_bytes(port))
    }

    /// Get the socket destination IPv4 and port.
    ///
    /// This is a compound which performs [`Registers::sn_dipr`] and
    /// [`Registers::sn_dport`] together.
    ///
    /// The `sn_dipr` and `sn_dport` registers are contiguous in memory, which
    /// allows this function to do one read transfer to read both registers.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x0C, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0, 0, 0, 0, 0], vec![0, 0, 0, 0, 0, 0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, net::SocketAddrV4, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let addr = w5500.sn_dest(Sn::Sn0)?;
    /// assert_eq!(addr, SocketAddrV4::default());
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_dest(&mut self, sn: Sn) -> Result<SocketAddrV4, Self::Error> {
        let mut buf: [u8; 6] = [0; 6];
        self.read(SnReg::DIPR0.addr(), sn.block(), &mut buf)?;
        Ok(SocketAddrV4::new(
            Ipv4Addr::new(buf[0], buf[1], buf[2], buf[3]),
            u16::from_be_bytes([buf[4], buf[5]]),
        ))
    }

    /// Set the socket destination IPv4 and port.
    ///
    /// This is a compound operation which performs
    /// [`Registers::set_sn_dipr`] and [`Registers::set_sn_dport`] together.
    ///
    /// The `sn_dipr` and `sn_dport` registers are contiguous in memory, which
    /// allows this function to do one write transfer to write both registers.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x0C, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![192, 168, 0, 11, 0, 67]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{
    ///     blocking::vdm::W5500,
    ///     net::{Ipv4Addr, SocketAddrV4},
    ///     Registers, Sn,
    /// };
    ///
    /// let addr: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 11), 67);
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_dest(Sn::Sn0, &addr)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_dest(&mut self, sn: Sn, addr: &SocketAddrV4) -> Result<(), Self::Error> {
        let buf: [u8; 6] = [
            addr.ip().octets[0],
            addr.ip().octets[1],
            addr.ip().octets[2],
            addr.ip().octets[3],
            (addr.port() >> 8) as u8,
            addr.port() as u8,
        ];
        self.write(SnReg::DIPR0.addr(), sn.block(), &buf)
    }

    /// Get the socket maximum segment size.
    ///
    /// This register configures or indicates the MTU (Maximum Transfer Unit)
    /// of the socket.
    ///
    /// The default MTU is valid when the socket is used in TCP / UDP mode.
    /// However, when used in PPPoE mode it is determined within the PPPoE MTU.
    ///
    /// | Mode   | Normal Default | Normal Range | PPPoE Default | PPPoE Range |
    /// |--------|----------------|--------------|---------------|-------------|
    /// | TCP    | 1460           | 1 - 1460     | 1452          | 1 - 1452    |
    /// | UDP    | 1472           | 1 - 1472     | 1464          | 1 - 1464    |
    /// | MACRAW | 1514           | 1514         | 1514          | 1514        |
    ///
    /// When socket n is used in MACRAW mode, the default MTU is applied
    /// because the MTU is not processed internally.
    /// Therefore, when transmitting the data bigger than default MTU, the host
    /// should manually divide the data into the unit of default MTU.
    /// When socket n is used in TCP/UDP mode, and transmitting data bigger than
    /// the MTU, the data is automatically divided into the unit of MTU.
    ///
    /// In UDP mode, the configured MTU is used.
    /// When transmitting data to a peer with the different MTU size,
    /// the ICMP (Fragment MTU) packet might be received.
    /// In this case, IR(FMTU) becomes `1` and the peer information such as the
    /// MTU size and IP address is indicated from FMTUR and UIPR respectively.
    /// If IR\[MTU\] = `1`, the user cannot transmit data to the peer.
    ///
    /// To resume the communication with peer, do as followed.
    /// 1. Close socket n with the [`SocketCommand::Close`] command.
    /// 2. Set Sn_MSS to the indicated MTU from FMTUR
    /// 3. Open socket n with the [`SocketCommand::Open`] command.
    /// 4. Resume the communication with the peer.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x12, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x00, 0x00]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn__mssr: u16 = w5500.sn_mssr(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_mssr(&mut self, sn: Sn) -> Result<u16, Self::Error> {
        let mut reg: [u8; 2] = [0; 2];
        self.read(SnReg::MSSR0.addr(), sn.block(), &mut reg)?;
        Ok(u16::from_be_bytes(reg))
    }

    /// Set the socket maximum segment size.
    ///
    /// See [`Registers::sn_mssr`] for lots more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x12, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x05, 0xB4]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_mssr(Sn::Sn0, 1460)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_mssr(&mut self, sn: Sn, mssr: u16) -> Result<(), Self::Error> {
        self.write(SnReg::MSSR0.addr(), sn.block(), &u16::to_be_bytes(mssr))
    }

    /// Get the IP type of service.
    ///
    /// This register configures the TOS (Type of service field in IP header)
    /// for socket n.
    /// Configure this field before sending the [`SocketCommand::Open`] command.
    ///
    /// For more details see [iana.org/assignments/ip-parameters].
    ///
    /// [iana.org/assignments/ip-parameters]: https://www.iana.org/assignments/ip-parameters
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x15, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let tos: u8 = w5500.sn_tos(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_tos(&mut self, sn: Sn) -> Result<u8, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(SnReg::TOS.addr(), sn.block(), &mut reg)?;
        Ok(reg[0])
    }

    /// Set the IP type of service.
    ///
    /// For more information see [`Registers::sn_tos`].
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x15, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x01]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_tos(Sn::Sn0, 1)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_tos(&mut self, sn: Sn, tos: u8) -> Result<(), Self::Error> {
        self.write(SnReg::TOS.addr(), sn.block(), &[tos])
    }

    /// Get the time to live.
    ///
    /// This register configures the TTL (Time to Live field in the IP header)
    /// for socket n.
    ///
    /// For more details see <https://www.iana.org/assignments/ip-parameters>.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x16, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0x80]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let ttl: u8 = w5500.sn_ttl(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_ttl(&mut self, sn: Sn) -> Result<u8, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(SnReg::TTL.addr(), sn.block(), &mut reg)?;
        Ok(reg[0])
    }

    /// Set the time to live.
    ///
    /// For more information see [`Registers::sn_ttl`].
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x16, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x80]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_ttl(Sn::Sn0, 0x80)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_ttl(&mut self, sn: Sn, ttl: u8) -> Result<(), Self::Error> {
        self.write(SnReg::TTL.addr(), sn.block(), &[ttl])
    }

    /// Get the socket RX buffer size.
    ///
    /// The buffer size can be configured to any of the sizes in [`BufferSize`].
    ///
    /// The sum of all the socket RX buffers cannot exceed 16 KiB.
    ///
    /// **Note:** This method returns a nested [`core::result::Result`].
    ///
    /// The outermost `Result` is for handling bus errors, similar to most of
    /// the other methods in this trait.
    ///
    /// The innermost `Result<BufferSize, u8>` is the result of a `u8` to
    /// [`BufferSize`] conversion because not every value of `u8` corresponds
    /// to a valid [`BufferSize`].
    /// * `u8` values that have a corresponding [`BufferSize`] will be
    ///   converted and returned in the [`Ok`] variant of the inner `Result`.
    /// * `u8` values that do not corresponding [`BufferSize`] will have the
    ///   raw `u8` byte returned in the [`Err`] variant of the inner `Result`.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1E, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0x02]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, BufferSize, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_rxbuf_size = w5500.sn_rxbuf_size(Sn::Sn0)?;
    /// assert_eq!(sn_rxbuf_size, Ok(BufferSize::KB2));
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    ///
    /// [`Ok`]: https://doc.rust-lang.org/core/result/enum.Result.html#variant.Ok
    /// [`Err`]: https://doc.rust-lang.org/core/result/enum.Result.html#variant.Err
    fn sn_rxbuf_size(&mut self, sn: Sn) -> Result<Result<BufferSize, u8>, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(SnReg::RXBUF_SIZE.addr(), sn.block(), &mut reg)?;
        Ok(BufferSize::try_from(reg[0]))
    }

    /// Set the socket RX buffer size.
    ///
    /// See [`Registers::sn_rxbuf_size`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1E, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![1]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, BufferSize, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_rxbuf_size(Sn::Sn0, BufferSize::KB1)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_rxbuf_size(&mut self, sn: Sn, size: BufferSize) -> Result<(), Self::Error> {
        self.write(SnReg::RXBUF_SIZE.addr(), sn.block(), &[size.into()])
    }

    /// Get the socket TX buffer size.
    ///
    /// The buffer size can be configured to any of the sizes in [`BufferSize`].
    ///
    /// The sum of all the socket TX buffers cannot exceed 16 KiB.
    ///
    /// **Note:** This method returns a nested [`core::result::Result`].
    ///
    /// The outermost `Result` is for handling bus errors, similar to most of
    /// the other methods in this trait.
    ///
    /// The innermost `Result<BufferSize, u8>` is the result of a `u8` to
    /// [`BufferSize`] conversion because not every value of `u8` corresponds
    /// to a valid [`BufferSize`].
    /// * `u8` values that have a corresponding [`BufferSize`] will be
    ///   converted and returned in the [`Ok`] variant of the inner `Result`.
    /// * `u8` values that do not corresponding [`BufferSize`] will have the
    ///   raw `u8` byte returned in the [`Err`] variant of the inner `Result`.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1F, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0x02]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, BufferSize, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_txbuf_size = w5500.sn_txbuf_size(Sn::Sn0)?;
    /// assert_eq!(sn_txbuf_size, Ok(BufferSize::KB2));
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    ///
    /// [`Ok`]: https://doc.rust-lang.org/core/result/enum.Result.html#variant.Ok
    /// [`Err`]: https://doc.rust-lang.org/core/result/enum.Result.html#variant.Err
    fn sn_txbuf_size(&mut self, sn: Sn) -> Result<Result<BufferSize, u8>, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(SnReg::TXBUF_SIZE.addr(), sn.block(), &mut reg)?;
        Ok(BufferSize::try_from(reg[0]))
    }

    /// Set the socket TX buffer size.
    ///
    /// See [`Registers::sn_rxbuf_size`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x1F, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![1]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, BufferSize, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_txbuf_size(Sn::Sn0, BufferSize::KB1)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_txbuf_size(&mut self, sn: Sn, size: BufferSize) -> Result<(), Self::Error> {
        self.write(SnReg::TXBUF_SIZE.addr(), sn.block(), &[size.into()])
    }

    /// Get transmit buffer free size.
    ///
    /// This register indicates the free size of socket n TX buffer.
    /// It is initialized to the configured size by [`Registers::sn_txbuf_size`].
    /// Data bigger than Sn_TX_FSR should not be written to the TX buffer to
    /// prevent buffer overflow.
    ///
    /// Check this register before writing data to the socket TX buffer,
    /// and if data is equal or smaller than its checked size, transmit the data
    /// with the [`SocketCommand::Send`] or [`SocketCommand::SendMac`] command
    /// after saving the data in Socket n TX buffer.
    ///
    /// If data is bigger than its checked size, transmit the data after
    /// dividing into the checked size and saving in the socket TX buffer.
    ///
    /// If [`Registers::sn_mr`] is not in TCP mode, this register is
    /// automatically calculated as the difference between
    /// [`Registers::sn_tx_wr`] and [`Registers::sn_tx_rd`].
    ///
    /// If [`Registers::sn_mr`] is in TCP mode, this register is automatically
    /// calculated as the difference between the internal ACK pointer which
    /// indicates the point of data is received already by the connected
    /// peer.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x20, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x08, 0x00]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketMode};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_tx_fsr: u16 = w5500.sn_tx_fsr(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_tx_fsr(&mut self, sn: Sn) -> Result<u16, Self::Error> {
        let mut reg: [u8; 2] = [0; 2];
        self.read(SnReg::TX_FSR0.addr(), sn.block(), &mut reg)?;
        Ok(u16::from_be_bytes(reg))
    }

    /// Get the socket TX read pointer.
    ///
    /// This register is initialized by the [`SocketCommand::Open`] command.
    /// However, in TCP mode, this is re-initialized while connecting with TCP.
    ///
    /// After initialization, this is auto-increased by the
    /// [`SocketCommand::Send`] command.
    ///
    /// The [`SocketCommand::Send`] command transmits the saved data from the
    /// current [`Registers::sn_tx_rd`] to the [`Registers::sn_tx_wr`] in the
    /// socket n TX buffer.
    /// After transmitting the saved data, the [`SocketCommand::Send`] command
    /// increases [`Registers::sn_tx_rd`] the as same as
    /// [`Registers::sn_tx_wr`].
    ///
    /// If its increment value exceeds the maximum value `0xFFFF`,
    /// (greater than `0x10000` and the carry bit occurs), then the carry bit is
    /// ignored and will automatically update with the lower 16 bits value.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x22, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x00, 0x00]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketMode};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_tx_rd: u16 = w5500.sn_tx_rd(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_tx_rd(&mut self, sn: Sn) -> Result<u16, Self::Error> {
        let mut reg: [u8; 2] = [0; 2];
        self.read(SnReg::TX_RD0.addr(), sn.block(), &mut reg)?;
        Ok(u16::from_be_bytes(reg))
    }

    /// Get the socket TX write pointer.
    ///
    /// This register is initialized by the [`SocketCommand::Open`] command.
    /// However, in TCP mode, this is re-initialized while connecting with TCP.
    ///
    /// This should be updated as follows:
    /// 1. Read the starting address for transmitting data.
    /// 2. Write data to the socket TX buffer buffer.
    /// 3. Update this register by the number of bytes written to the TX buffer.
    ///    Allow wrapping to occur upon `u16` overflow.
    /// 4. Transmit the saved data in the socket TX buffer by using the
    ///    [`SocketCommand::Send`] command.
    ///
    /// See [`Registers::set_sn_tx_buf`] for an additional example.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x24, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x00, 0x00]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_tx_wr: u16 = w5500.sn_tx_wr(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_tx_wr(&mut self, sn: Sn) -> Result<u16, Self::Error> {
        let mut reg: [u8; 2] = [0; 2];
        self.read(SnReg::TX_WR0.addr(), sn.block(), &mut reg)?;
        Ok(u16::from_be_bytes(reg))
    }

    /// Set the socket TX write pointer.
    ///
    /// See [`Registers::sn_tx_wr`] for more information.
    ///
    /// See [`Registers::set_sn_tx_buf`] for an example.
    fn set_sn_tx_wr(&mut self, sn: Sn, ptr: u16) -> Result<(), Self::Error> {
        self.write(SnReg::TX_WR0.addr(), sn.block(), &ptr.to_be_bytes())
    }

    /// Get the socket received data size.
    ///
    /// This register indicates the data size received and saved in the socket
    /// RX buffer.
    /// This register does not exceed the configured size
    /// ([`Registers::set_sn_rxbuf_size`]) and is calculated as the difference
    /// between [`Registers::sn_rx_wr`] and [`Registers::sn_rx_rd`].
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x26, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x00, 0x00]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_rx_rsr: u16 = w5500.sn_rx_rsr(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_rx_rsr(&mut self, sn: Sn) -> Result<u16, Self::Error> {
        let mut reg: [u8; 2] = [0; 2];
        self.read(SnReg::RX_RSR0.addr(), sn.block(), &mut reg)?;
        Ok(u16::from_be_bytes(reg))
    }

    /// Get the socket read data pointer.
    ///
    /// This register is initialized by the [`SocketCommand::Open`] command.
    ///
    /// This should be updated as follows:
    /// 1. Read the starting address for reading data.
    /// 2. Read from the socket RX buffer.
    /// 3. Update this register by the number of bytes read from the RX buffer.
    ///    Allow wrapping to occur upon `u16` overflow.
    /// 4. Send a [`SocketCommand::Recv`] command to notify the W5500 of the
    ///    retrieved data.
    ///
    /// See [`Registers::sn_rx_buf`] for an additional example.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x28, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x00, 0x00]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_rx_rd: u16 = w5500.sn_rx_rd(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_rx_rd(&mut self, sn: Sn) -> Result<u16, Self::Error> {
        let mut reg: [u8; 2] = [0; 2];
        self.read(SnReg::RX_RD0.addr(), sn.block(), &mut reg)?;
        Ok(u16::from_be_bytes(reg))
    }

    /// Set the socket read data pointer.
    ///
    /// See [`Registers::sn_rx_rd`] for more information.
    /// See [`Registers::sn_rx_buf`] for an example.
    fn set_sn_rx_rd(&mut self, sn: Sn, ptr: u16) -> Result<(), Self::Error> {
        self.write(SnReg::RX_RD0.addr(), sn.block(), &ptr.to_be_bytes())
    }

    /// Get the socket RX write pointer.
    ///
    /// This register is initialized by the [`SocketCommand::Open`] command, and
    /// it is auto-incremented by hardware.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x2A, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x00, 0x00]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_rx_wr: u16 = w5500.sn_rx_wr(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_rx_wr(&mut self, sn: Sn) -> Result<u16, Self::Error> {
        let mut reg: [u8; 2] = [0; 2];
        self.read(SnReg::RX_WR0.addr(), sn.block(), &mut reg)?;
        Ok(u16::from_be_bytes(reg))
    }

    /// Get the socket interrupt mask.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x2C, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0xFF]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketInterruptMask};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_imr: SocketInterruptMask = w5500.sn_imr(Sn::Sn0)?;
    /// assert_eq!(sn_imr, SocketInterruptMask::default());
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_imr(&mut self, sn: Sn) -> Result<SocketInterruptMask, Self::Error> {
        let mut reg: [u8; 1] = [0];
        self.read(SnReg::IMR.addr(), sn.block(), &mut reg)?;
        Ok(SocketInterruptMask::from(reg[0]))
    }

    /// Set the socket interrupt mask.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x2C, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0xE0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketInterruptMask};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_imr(Sn::Sn0, SocketInterruptMask::ALL_MASKED)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_imr(&mut self, sn: Sn, mask: SocketInterruptMask) -> Result<(), Self::Error> {
        self.write(SnReg::IMR.addr(), sn.block(), &[mask.into()])
    }

    /// Get the socket fragment.
    ///
    /// This configures the fragment field in the IP header.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x2D, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x40, 0x00]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let frag: u16 = w5500.sn_frag(Sn::Sn0)?;
    /// assert_eq!(frag, 0x4000);
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_frag(&mut self, sn: Sn) -> Result<u16, Self::Error> {
        let mut buf: [u8; 2] = [0; 2];
        self.read(SnReg::FRAG0.addr(), sn.block(), &mut buf)?;
        Ok(u16::from_be_bytes(buf))
    }

    /// Set the socket fragment.
    ///
    /// See [`Registers::sn_frag`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x2D, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x00, 0x00]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// w5500.set_sn_frag(Sn::Sn0, 0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_frag(&mut self, sn: Sn, frag: u16) -> Result<(), Self::Error> {
        self.write(SnReg::FRAG0.addr(), sn.block(), &u16::to_be_bytes(frag))
    }

    /// Get the socket keep alive time.
    ///
    /// This register configures the transmitting timer of the keep alive (KA)
    /// packet for the socket.  This is valid only in TCP mode, and is ignored
    /// in all other modes.
    ///
    /// The time unit is 5 seconds.
    ///
    /// The KA packet is transmittable after [`Registers::sn_sr`] is changed to
    /// [`SocketStatus::Established`] and after the data is transmitted or
    /// received to/from a peer at least once.
    ///
    /// In the case of a non-zero keep alive value the W5500 automatically
    /// transmits a KA packet after time-period for checking the TCP connection
    /// (automatic-keepalive-process).
    ///
    /// In case of a zero keep alive value, the keep alive packet can be
    /// transmitted with [`SocketCommand::SendKeep`].  This command is ignored
    /// for non-zero keep alive values.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x2F, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// let sn_kpalvtr: u8 = w5500.sn_kpalvtr(Sn::Sn0)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_kpalvtr(&mut self, sn: Sn) -> Result<u8, Self::Error> {
        let mut buf: [u8; 1] = [0];
        self.read(SnReg::KPALVTR.addr(), sn.block(), &mut buf)?;
        Ok(buf[0])
    }

    /// Set the socket keep alive time.
    ///
    /// See [`Registers::sn_kpalvtr`] for more information.
    ///
    /// # Example
    ///
    /// ```
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x2F, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x0A]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn};
    ///
    /// let mut w5500 = W5500::new(spi, pin);
    /// // 50s keep alive timer
    /// w5500.set_sn_kpalvtr(Sn::Sn0, 10)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_kpalvtr(&mut self, sn: Sn, kpalvtr: u8) -> Result<(), Self::Error> {
        self.write(SnReg::KPALVTR.addr(), sn.block(), &[kpalvtr])
    }

    /// Write the socket TX buffer.
    ///
    /// # Example
    ///
    /// ```
    /// use core::cmp::min;
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketCommand};
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, w5500_ll::SnReg::TX_FSR0.addr() as u8, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x08, 0x00]),
    /// #   hal::spi::Transaction::write(vec![0x00, w5500_ll::SnReg::TX_WR0.addr() as u8, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0x00, 0x00]),
    /// #   hal::spi::Transaction::write(vec![0x00, 0x00, (Sn::Sn0.tx_block() as u8) << 3 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x12, 0x34, 0x56, 0x78, 0x9A]),
    /// #   hal::spi::Transaction::write(vec![0x00, 0x24, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0x00, 5]),
    /// #   hal::spi::Transaction::write(vec![0x00, 0x01, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![SocketCommand::Send.into()]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// # let mut w5500 = W5500::new(spi, pin);
    ///
    /// // the socket should already be opened at this point
    /// const THE_SOCKET: Sn = Sn::Sn0;
    ///
    /// let buf: [u8; 5] = [0x12, 0x34, 0x56, 0x78, 0x9A];
    ///
    /// // transmit as many bytes as possible
    /// // for large buffers this may not transmit all the available data
    /// let tx_bytes: u16 = {
    ///     min(w5500.sn_tx_fsr(THE_SOCKET)?, u16::try_from(buf.len()).unwrap_or(u16::MAX))
    /// };
    /// if tx_bytes == 0 {
    ///     return Ok(());
    /// }
    ///
    /// let ptr: u16 = w5500.sn_tx_wr(THE_SOCKET)?;
    /// w5500.set_sn_tx_buf(THE_SOCKET, ptr, &buf[..usize::from(tx_bytes)])?;
    /// w5500.set_sn_tx_wr(THE_SOCKET, ptr.wrapping_add(tx_bytes))?;
    /// w5500.set_sn_cr(THE_SOCKET, SocketCommand::Send)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_tx_buf(&mut self, sn: Sn, ptr: u16, buf: &[u8]) -> Result<(), Self::Error> {
        self.write(ptr, sn.tx_block(), buf)
    }

    /// Read the socket TX buffer.
    ///
    /// This method is typically unused; there are very few use cases that
    /// require reading the TX buffer.
    ///
    /// # Example
    ///
    /// ```
    /// use core::cmp::min;
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketCommand};
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x00, (Sn::Sn0.tx_block() as u8) << 3]),
    /// #   hal::spi::Transaction::transfer(vec![0], vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// # ]);
    /// # let mut w5500 = W5500::new(spi, pin);
    /// let mut buf: [u8; 1] = [0];
    /// w5500.sn_tx_buf(Sn::Sn0, 0, &mut buf)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_tx_buf(&mut self, sn: Sn, ptr: u16, buf: &mut [u8]) -> Result<(), Self::Error> {
        self.read(ptr, sn.tx_block(), buf)
    }

    /// Read the socket RX buffer.
    ///
    /// # Example
    ///
    /// ```
    /// use core::cmp::min;
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketCommand};
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, w5500_ll::SnReg::RX_RSR0.addr() as u8, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0, 4]),
    /// #   hal::spi::Transaction::write(vec![0x00, w5500_ll::SnReg::RX_RD0.addr() as u8, 0x08]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0], vec![0, 0]),
    /// #   hal::spi::Transaction::write(vec![0x00, 0x00, (Sn::Sn0.rx_block() as u8) << 3]),
    /// #   hal::spi::Transaction::transfer(vec![0, 0, 0, 0], vec![0, 0, 0, 0]),
    /// #   hal::spi::Transaction::write(vec![0x00, w5500_ll::SnReg::RX_RD0.addr() as u8, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0, 4]),
    /// #   hal::spi::Transaction::write(vec![0x00, w5500_ll::SnReg::CR.addr() as u8, 0x08 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![SocketCommand::Recv.into()]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// # ]);
    /// # let mut w5500 = W5500::new(spi, pin);
    ///
    /// // the socket should already be opened at this point
    /// // a socket interrupt will indicate there is data to be retrieved
    /// const THE_SOCKET: Sn = Sn::Sn0;
    ///
    /// // in reality you will need a larger buffer for most protocols
    /// const BUF_LEN: usize = 16;
    /// let mut buf: [u8; BUF_LEN] = [0; BUF_LEN];
    ///
    /// let rx_bytes: u16 = {
    ///     min(w5500.sn_rx_rsr(THE_SOCKET)?, u16::try_from(buf.len()).unwrap_or(u16::MAX))
    /// };
    /// if rx_bytes == 0 {
    ///     return Ok(());
    /// }
    ///
    /// let ptr: u16 = w5500.sn_rx_rd(THE_SOCKET)?;
    /// w5500.sn_rx_buf(THE_SOCKET, ptr, &mut buf[..usize::from(rx_bytes)])?;
    /// w5500.set_sn_rx_rd(THE_SOCKET, ptr.wrapping_add(rx_bytes))?;
    /// w5500.set_sn_cr(THE_SOCKET, SocketCommand::Recv)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn sn_rx_buf(&mut self, sn: Sn, ptr: u16, buf: &mut [u8]) -> Result<(), Self::Error> {
        self.read(ptr, sn.rx_block(), buf)
    }

    /// Write the socket RX buffer.
    ///
    /// This method is typically unused; there are very few use cases that
    /// require writing the RX buffer.
    ///
    /// # Example
    ///
    /// ```
    /// use core::cmp::min;
    /// use w5500_ll::{blocking::vdm::W5500, Registers, Sn, SocketCommand};
    /// # use embedded_hal_mock as hal;
    /// # let spi = hal::spi::Mock::new(&[
    /// #   hal::spi::Transaction::write(vec![0x00, 0x00, (Sn::Sn0.rx_block() as u8) << 3 | 0x04]),
    /// #   hal::spi::Transaction::write(vec![0]),
    /// # ]);
    /// # let pin = hal::pin::Mock::new(&[
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// #    hal::pin::Transaction::set(hal::pin::State::High),
    /// #    hal::pin::Transaction::set(hal::pin::State::Low),
    /// # ]);
    /// # let mut w5500 = W5500::new(spi, pin);
    /// let buf: [u8; 1] = [0];
    /// w5500.set_sn_rx_buf(Sn::Sn0, 0, &buf)?;
    /// # Ok::<(), w5500_ll::blocking::vdm::Error<_, _>>(())
    /// ```
    fn set_sn_rx_buf(&mut self, sn: Sn, ptr: u16, buf: &[u8]) -> Result<(), Self::Error> {
        self.write(ptr, sn.rx_block(), buf)
    }
}