aboutsummaryrefslogtreecommitdiff
path: root/embassy-net-wiznet/src/lib.rs
blob: 3fbd4c741e2942f91eb4da2ce26238f790be332b (plain)
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
#![no_std]
#![allow(async_fn_in_trait)]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]

pub mod chip;
mod device;

use embassy_futures::select::{select3, Either3};
use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::LinkState;
use embassy_time::{Duration, Ticker, Timer};
use embedded_hal::digital::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiDevice;

use crate::chip::Chip;
pub use crate::device::InitError;
use crate::device::WiznetDevice;

// If you change this update the docs of State
const MTU: usize = 1514;

/// Type alias for the embassy-net driver.
pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;

/// Internal state for the embassy-net integration.
///
/// The two generic arguments `N_RX` and `N_TX` set the size of the receive and
/// send packet queue. With a the ethernet MTU of _1514_ this takes up `N_RX +
/// NTX * 1514` bytes. While setting these both to 1 is the minimum this might
/// hurt performance as a packet can not be received while processing another.
///
/// # Warning
/// On devices with a small amount of ram (think ~64k) watch out with the size
/// of there parameters. They will quickly use too much RAM.
pub struct State<const N_RX: usize, const N_TX: usize> {
    ch_state: ch::State<MTU, N_RX, N_TX>,
}

impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {
    /// Create a new `State`.
    pub const fn new() -> Self {
        Self {
            ch_state: ch::State::new(),
        }
    }
}

/// Background runner for the driver.
///
/// You must call `.run()` in a background task for the driver to operate.
pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> {
    mac: WiznetDevice<C, SPI>,
    ch: ch::Runner<'d, MTU>,
    int: INT,
    _reset: RST,
}

/// You must call this in a background task for the driver to operate.
impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> {
    /// Run the driver.
    pub async fn run(mut self) -> ! {
        let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
        let mut tick = Ticker::every(Duration::from_millis(500));
        loop {
            match select3(
                async {
                    self.int.wait_for_low().await.ok();
                    rx_chan.rx_buf().await
                },
                tx_chan.tx_buf(),
                tick.next(),
            )
            .await
            {
                Either3::First(p) => {
                    if let Ok(n) = self.mac.read_frame(p).await {
                        rx_chan.rx_done(n);
                    }
                }
                Either3::Second(p) => {
                    self.mac.write_frame(p).await.ok();
                    tx_chan.tx_done();
                }
                Either3::Third(()) => {
                    if self.mac.is_link_up().await {
                        state_chan.set_link_state(LinkState::Up);
                    } else {
                        state_chan.set_link_state(LinkState::Down);
                    }
                }
            }
        }
    }
}

/// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net).
///
/// This returns two structs:
/// - a `Device` that you must pass to the `embassy-net` stack.
/// - a `Runner`. You must call `.run()` on it in a background task.
pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
    mac_addr: [u8; 6],
    state: &'a mut State<N_RX, N_TX>,
    spi_dev: SPI,
    int: INT,
    mut reset: RST,
) -> Result<(Device<'a>, Runner<'a, C, SPI, INT, RST>), InitError<SPI::Error>> {
    // Reset the chip.
    reset.set_low().ok();
    // Ensure the reset is registered.
    Timer::after_millis(1).await;
    reset.set_high().ok();

    // Wait for PLL lock. Some chips are slower than others.
    // Slowest is w5100s which is 100ms, so let's just wait that.
    Timer::after_millis(100).await;

    let mac = WiznetDevice::new(spi_dev, mac_addr).await?;

    let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr));

    Ok((
        device,
        Runner {
            ch: runner,
            mac,
            int,
            _reset: reset,
        },
    ))
}