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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#![no_std]

pub mod spi;
use embedded_hal::{
    blocking::{delay::DelayUs, spi::Transfer},
    digital::v2::OutputPin,
};

pub mod rx;
pub mod tx;

#[cfg(feature = "smoltcp")]
pub mod smoltcp_phy;

/// Max raw frame array size
pub const RAW_FRAME_LENGTH_MAX: usize = 1518;

/// Trait representing PHY layer of ENC424J600
pub trait EthPhy {
    fn recv_packet(&mut self, is_poll: bool) -> Result<rx::RxPacket, Error>;
    fn send_packet(&mut self, packet: &tx::TxPacket) -> Result<(), Error>;
}

/// TODO: Improve these error types
#[derive(Debug)]
pub enum Error {
    SpiPortError,
    RegisterError,
    // TODO: Better name?
    NoRxPacketError,
}

impl From<spi::Error> for Error {
    fn from(_: spi::Error) -> Error {
        Error::SpiPortError
    }
}

/// ENC424J600 controller in SPI mode
pub struct Enc424j600<SPI: Transfer<u8>, NSS: OutputPin> {
    spi_port: spi::SpiPort<SPI, NSS>,
    rx_buf: rx::RxBuffer,
    tx_buf: tx::TxBuffer,
}

impl<SPI: Transfer<u8>, NSS: OutputPin> Enc424j600<SPI, NSS> {
    pub fn new(spi: SPI, nss: NSS) -> Self {
        Enc424j600 {
            spi_port: spi::SpiPort::new(spi, nss),
            rx_buf: rx::RxBuffer::new(),
            tx_buf: tx::TxBuffer::new(),
        }
    }

    #[cfg(feature = "cortex-m-cpu")]
    pub fn cpu_freq_mhz(mut self, freq: u32) -> Self {
        self.spi_port = self.spi_port.cpu_freq_mhz(freq);
        self
    }

    pub fn init(&mut self, delay: &mut impl DelayUs<u16>) -> Result<(), Error> {
        self.reset(delay)?;
        self.init_rxbuf()?;
        self.init_txbuf()?;
        Ok(())
    }

    pub fn reset(&mut self, delay: &mut impl DelayUs<u16>) -> Result<(), Error> {
        // Write 0x1234 to EUDAST
        self.spi_port.write_reg_16b(spi::addrs::EUDAST, 0x1234)?;
        // Verify that EUDAST is 0x1234
        let mut eudast = self.spi_port.read_reg_16b(spi::addrs::EUDAST)?;
        if eudast != 0x1234 {
            return Err(Error::RegisterError);
        }
        // Poll CLKRDY (ESTAT<12>) to check if it is set
        loop {
            let estat = self.spi_port.read_reg_16b(spi::addrs::ESTAT)?;
            if estat & 0x1000 == 0x1000 {
                break;
            }
        }
        // Issue system reset - set ETHRST (ECON2<4>) to 1
        self.spi_port.send_opcode(spi::opcodes::SETETHRST)?;
        delay.delay_us(25);
        // Verify that EUDAST is 0x0000
        eudast = self.spi_port.read_reg_16b(spi::addrs::EUDAST)?;
        if eudast != 0x0000 {
            return Err(Error::RegisterError);
        }
        delay.delay_us(256);
        Ok(())
    }

    pub fn init_rxbuf(&mut self) -> Result<(), Error> {
        // Set ERXST pointer
        self.spi_port
            .write_reg_16b(spi::addrs::ERXST, self.rx_buf.get_start_addr())?;
        // Set ERXTAIL pointer
        self.spi_port
            .write_reg_16b(spi::addrs::ERXTAIL, self.rx_buf.get_tail_addr())?;
        // Set MAMXFL to maximum number of bytes in each accepted packet
        self.spi_port
            .write_reg_16b(spi::addrs::MAMXFL, RAW_FRAME_LENGTH_MAX as u16)?;
        // Enable RX - set RXEN (ECON1<0>) to 1
        self.spi_port.send_opcode(spi::opcodes::ENABLERX)?;
        Ok(())
    }

    pub fn init_txbuf(&mut self) -> Result<(), Error> {
        // Set EGPWRPT pointer
        self.spi_port.write_reg_16b(spi::addrs::EGPWRPT, 0x0000)?;
        Ok(())
    }

    /// Set controller to Promiscuous Mode
    pub fn set_promiscuous(&mut self) -> Result<(), Error> {
        // From Section 10.12, ENC424J600 Data Sheet:
        // "To accept all incoming frames regardless of content (Promiscuous mode),
        // set the CRCEN, RUNTEN, UCEN, NOTMEEN and MCEN bits."
        let erxfcon_lo = self.spi_port.read_reg_8b(spi::addrs::ERXFCON)?;
        self.spi_port.write_reg_8b(
            spi::addrs::ERXFCON,
            0b0101_1110 | (erxfcon_lo & 0b1010_0001),
        )?;
        Ok(())
    }

    /// Read MAC to [u8; 6]
    pub fn read_mac_addr(&mut self, mac: &mut [u8]) -> Result<(), Error> {
        mac[0] = self.spi_port.read_reg_8b(spi::addrs::MAADR1)?;
        mac[1] = self.spi_port.read_reg_8b(spi::addrs::MAADR1 + 1)?;
        mac[2] = self.spi_port.read_reg_8b(spi::addrs::MAADR2)?;
        mac[3] = self.spi_port.read_reg_8b(spi::addrs::MAADR2 + 1)?;
        mac[4] = self.spi_port.read_reg_8b(spi::addrs::MAADR3)?;
        mac[5] = self.spi_port.read_reg_8b(spi::addrs::MAADR3 + 1)?;
        Ok(())
    }

    pub fn write_mac_addr(&mut self, mac: &[u8]) -> Result<(), Error> {
        self.spi_port.write_reg_8b(spi::addrs::MAADR1, mac[0])?;
        self.spi_port.write_reg_8b(spi::addrs::MAADR1 + 1, mac[1])?;
        self.spi_port.write_reg_8b(spi::addrs::MAADR2, mac[2])?;
        self.spi_port.write_reg_8b(spi::addrs::MAADR2 + 1, mac[3])?;
        self.spi_port.write_reg_8b(spi::addrs::MAADR3, mac[4])?;
        self.spi_port.write_reg_8b(spi::addrs::MAADR3 + 1, mac[5])?;
        Ok(())
    }
}

impl<SPI: Transfer<u8>, NSS: OutputPin> EthPhy for Enc424j600<SPI, NSS> {
    /// Receive the next packet and return it
    /// Set is_poll to true for returning until PKTIF is set;
    /// Set is_poll to false for returning Err when PKTIF is not set
    fn recv_packet(&mut self, is_poll: bool) -> Result<rx::RxPacket, Error> {
        // Poll PKTIF (EIR<4>) to check if it is set
        loop {
            let eir = self.spi_port.read_reg_16b(spi::addrs::EIR)?;
            if eir & 0x40 == 0x40 {
                break;
            }
            if !is_poll {
                return Err(Error::NoRxPacketError);
            }
        }
        // Set ERXRDPT pointer to next_addr
        self.spi_port
            .write_reg_16b(spi::addrs::ERXRDPT, self.rx_buf.get_next_addr())?;
        // Read 2 bytes to update next_addr
        let mut next_addr_buf = [0; 3];
        self.spi_port.read_rxdat(&mut next_addr_buf, 2)?;
        self.rx_buf
            .set_next_addr((next_addr_buf[1] as u16) | ((next_addr_buf[2] as u16) << 8));
        // Read 6 bytes to update rsv
        let mut rsv_buf = [0; 7];
        self.spi_port.read_rxdat(&mut rsv_buf, 6)?;
        // Construct an RxPacket
        // TODO: can we directly assign to fields instead of using functions?
        let mut rx_packet = rx::RxPacket::new();
        // Get and update frame length
        rx_packet.write_to_rsv(&rsv_buf[1..]);
        rx_packet.update_frame_length();
        // Read frame bytes
        let mut frame_buf = [0; RAW_FRAME_LENGTH_MAX];
        self.spi_port
            .read_rxdat(&mut frame_buf, rx_packet.get_frame_length())?;
        rx_packet.copy_frame_from(&frame_buf[1..]);
        // Set ERXTAIL pointer to (next_addr - 2)
        // * Assume head, tail, next and wrap addresses are word-aligned (even)
        // - If next_addr is at least (start_addr+2), then set tail pointer to the word right before next_addr
        if self.rx_buf.get_next_addr() > self.rx_buf.get_start_addr() {
            self.spi_port
                .write_reg_16b(spi::addrs::ERXTAIL, self.rx_buf.get_next_addr() - 2)?;
        // - Otherwise, next_addr will wrap, so set tail pointer to the last word address of RX buffer
        } else {
            self.spi_port
                .write_reg_16b(spi::addrs::ERXTAIL, rx::RX_MAX_ADDRESS - 1)?;
        }
        // Decrement PKTCNT - set PKTDEC (ECON1<8>)
        self.spi_port.send_opcode(spi::opcodes::SETPKTDEC)?;
        // Return the RxPacket
        Ok(rx_packet)
    }

    /// Send an established packet
    fn send_packet(&mut self, packet: &tx::TxPacket) -> Result<(), Error> {
        // Set EGPWRPT pointer to next_addr
        self.spi_port
            .write_reg_16b(spi::addrs::EGPWRPT, self.tx_buf.get_next_addr())?;
        // Copy packet data to SRAM Buffer
        // 1-byte Opcode is included
        let mut txdat_buf: [u8; RAW_FRAME_LENGTH_MAX + 1] = [0; RAW_FRAME_LENGTH_MAX + 1];
        packet.write_frame_to(&mut txdat_buf[1..]);
        self.spi_port
            .write_txdat(&mut txdat_buf, packet.get_frame_length())?;
        // Set ETXST to packet start address
        self.spi_port
            .write_reg_16b(spi::addrs::ETXST, self.tx_buf.get_next_addr())?;
        // Set ETXLEN to packet length
        self.spi_port
            .write_reg_16b(spi::addrs::ETXLEN, packet.get_frame_length() as u16)?;
        // Send packet - set TXRTS (ECON1<1>) to start transmission
        self.spi_port.send_opcode(spi::opcodes::SETTXRTS)?;
        // Poll TXRTS (ECON1<1>) to check if it is reset
        loop {
            let econ1_lo = self.spi_port.read_reg_8b(spi::addrs::ECON1)?;
            if econ1_lo & 0x02 == 0 {
                break;
            }
        }
        // TODO: Read ETXSTAT to understand Ethernet transmission status
        // (See: Register 9-2, ENC424J600 Data Sheet)
        // Update TX buffer start address
        // * Assume TX buffer consumes the entire general-purpose SRAM block
        self.tx_buf.set_next_addr(
            (self.tx_buf.get_next_addr() + packet.get_frame_length() as u16)
                % self.rx_buf.get_start_addr()
                - self.tx_buf.get_start_addr(),
        );
        Ok(())
    }
}