1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! Fixed data length implementation of the [`Registers`] trait using the
//! [`embedded-hal`] blocking SPI trait.
//!
//! This uses the W5500 fixed data length mode (FDM).
//! In FSM mode the SPI chip select pin is always tied low, and it is not
//! possible to share the bus with other devices.
//!
//! If possible, you should use the [VDM] implementation instead.
//!
//! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
//! [`Registers`]: crate::Registers
//! [VDM]: crate::eh1::vdm

use crate::spi::{self, AccessMode};

/// W5500 blocking fixed data length implementation.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct W5500<SPI> {
    /// SPI bus.
    spi: SPI,
}

impl<SPI> W5500<SPI>
where
    SPI: eh1::spi::SpiBus<u8>,
{
    /// Creates a new `W5500` driver from a SPI bus.
    ///
    /// # Example
    ///
    /// ```
    /// # use ehm::eh1 as hal;
    /// # let spi = hal::spi::Mock::new(&[]);
    /// use w5500_ll::eh1::fdm::W5500;
    ///
    /// let mut w5500: W5500<_> = W5500::new(spi);
    /// # w5500.free().done();
    /// ```
    #[inline]
    pub fn new(spi: SPI) -> Self {
        W5500 { spi }
    }

    /// Free the SPI bus from the W5500.
    ///
    /// # Example
    ///
    /// ```
    /// # use ehm::eh1 as hal;
    /// # let spi = hal::spi::Mock::new(&[]);
    /// use w5500_ll::eh1::fdm::W5500;
    ///
    /// let w5500: W5500<_> = W5500::new(spi);
    /// let mut spi = w5500.free();
    /// # spi.done();
    /// ```
    #[inline]
    pub fn free(self) -> SPI {
        self.spi
    }
}

impl<SPI> crate::Registers for W5500<SPI>
where
    SPI: eh1::spi::SpiBus<u8>,
{
    /// SPI IO error type.
    type Error = SPI::Error;

    /// Read from the W5500.
    #[allow(clippy::while_let_on_iterator)]
    fn read(&mut self, mut address: u16, block: u8, data: &mut [u8]) -> Result<(), Self::Error> {
        let mut chunks = data.chunks_exact_mut(4);
        while let Some(chunk) = chunks.next() {
            let header = spi::fdm_header_4b(address, block, AccessMode::Read);
            self.spi.write(&header)?;
            self.spi.read(chunk)?;
            address = address.wrapping_add(4);
        }
        let mut chunks = chunks.into_remainder().chunks_exact_mut(2);
        while let Some(chunk) = chunks.next() {
            let header = spi::fdm_header_2b(address, block, AccessMode::Read);
            self.spi.write(&header)?;
            self.spi.read(chunk)?;
            address = address.wrapping_add(2);
        }
        let mut chunks = chunks.into_remainder().chunks_exact_mut(1);
        while let Some(chunk) = chunks.next() {
            let header = spi::fdm_header_1b(address, block, AccessMode::Read);
            self.spi.write(&header)?;
            self.spi.read(chunk)?;
            address = address.wrapping_add(1);
        }

        Ok(())
    }

    /// Write to the W5500.
    #[allow(clippy::while_let_on_iterator)]
    fn write(&mut self, mut address: u16, block: u8, data: &[u8]) -> Result<(), Self::Error> {
        let mut chunks = data.chunks_exact(4);
        while let Some(chunk) = chunks.next() {
            let header = spi::fdm_header_4b(address, block, AccessMode::Write);
            self.spi.write(&header)?;
            self.spi.write(chunk)?;
            address = address.wrapping_add(4);
        }
        let mut chunks = chunks.remainder().chunks_exact(2);
        while let Some(chunk) = chunks.next() {
            let header = spi::fdm_header_2b(address, block, AccessMode::Write);
            self.spi.write(&header)?;
            self.spi.write(chunk)?;
            address = address.wrapping_add(2);
        }
        let mut chunks = chunks.remainder().chunks_exact(1);
        while let Some(chunk) = chunks.next() {
            let header = spi::fdm_header_1b(address, block, AccessMode::Write);
            self.spi.write(&header)?;
            self.spi.write(chunk)?;
            address = address.wrapping_add(1);
        }

        Ok(())
    }
}