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
//! A bit-banged implementation of the MDIO traits.

use crate::{Read, Write};
use embedded_hal::digital::v2::{InputPin, OutputPin};
use embedded_hal::timer::{CountDown, Periodic};
use nb::block;

/// A type providing a "bit-banged" MDIO implementation around two given GPIO pins.
///
/// ## Read
///
/// The `mdio::Read` implementation works as follows:
///
/// - Writes the 32-bit preamble.
/// - Writes the 14 most significant bits of the given `ctrl_bits` in MSB order.
/// - Waits for 2 bit times for the turn around.
/// - Reads the 16-bit data using `u16::from_be_bytes`.
///
/// ## Write
///
/// The `mdio::Write` implementation works as follows:
///
/// - Writes the 32-bit preamble.
/// - Writes the 16-bit ctrl value in MSB order.
/// - Writes the 16-bit data in MSB order.
pub struct Mdio<MdioPin, MdcPin, Clk> {
    /// The data pin.
    mdio: MdioPin,
    /// The clock pin.
    mdc: MdcPin,
    /// The clock used to time bangs.
    clk: Clk,
}

impl<MdioPin, MdcPin, Clk, E> Mdio<MdioPin, MdcPin, Clk>
where
    MdcPin: OutputPin<Error = E>,
    MdioPin: InputPin<Error = E> + OutputPin<Error = E>,
    Clk: CountDown + Periodic,
{
    /// The duration of the preamble in bits.
    const PREAMBLE_BITS: usize = 32;

    /// Create the bit-banged MDIO instance.
    pub fn new(mdio: MdioPin, mdc: MdcPin, clk: Clk) -> Self {
        Self { mdio, mdc, clk }
    }

    /// Split the MDIO bit-banged implementation into its parts.
    pub fn into_parts(self) -> (MdioPin, MdcPin, Clk) {
        let Self { mdio, mdc, clk } = self;
        (mdio, mdc, clk)
    }

    fn wait_for_clk(&mut self) {
        block!(self.clk.wait()).ok();
    }

    fn pulse_clock(&mut self) -> Result<(), E> {
        self.wait_for_clk();
        self.mdc.set_high()?;
        self.wait_for_clk();
        self.mdc.set_low()
    }

    fn preamble(&mut self) -> Result<(), E> {
        self.mdio.set_high()?;
        for _bit in 0..Self::PREAMBLE_BITS {
            self.pulse_clock()?;
        }
        Ok(())
    }

    fn write_bit(&mut self, bit: bool) -> Result<(), E> {
        if bit {
            self.mdio.set_high()?;
        } else {
            self.mdio.set_low()?;
        }
        self.pulse_clock()
    }

    /// Write `count` number of most significant bits in the given byte.
    fn write_bits(&mut self, byte: u8, count: usize) -> Result<(), E> {
        for bit_offset in 0..core::cmp::min(count, 8) {
            self.write_bit(bit_is_set(byte, bit_offset))?;
        }
        Ok(())
    }

    /// Write each bit in the byte, starting with the most significant.
    fn write_u8(&mut self, byte: u8) -> Result<(), E> {
        for bit_offset in 0..8 {
            self.write_bit(bit_is_set(byte, bit_offset))?;
        }
        Ok(())
    }

    /// Write both bytes in the given `u16`, starting with the most significant.
    fn write_u16(&mut self, bytes: u16) -> Result<(), E> {
        for &byte in &bytes.to_be_bytes() {
            self.write_u8(byte)?;
        }
        Ok(())
    }

    /// Wait for the turnaround before reading.
    fn turnaround(&mut self) -> Result<(), E> {
        // TODO: Is anything needed to release Mdio pin here?
        self.pulse_clock()?;
        self.pulse_clock()
    }

    fn read_bit(&mut self) -> Result<bool, E> {
        let b = self.mdio.is_high()?;
        self.pulse_clock()?;
        Ok(b)
    }

    fn read_byte(&mut self) -> Result<u8, E> {
        let mut byte = 0u8;
        for bit_offset in 0..8 {
            if self.read_bit()? {
                set_bit(&mut byte, bit_offset);
            }
        }
        Ok(byte)
    }

    fn read_u16(&mut self) -> Result<u16, E> {
        let a = self.read_byte()?;
        let b = self.read_byte()?;
        let u = u16::from_be_bytes([a, b]);
        Ok(u)
    }
}

impl<MdioPin, MdcPin, Clk, E> Read for Mdio<MdioPin, MdcPin, Clk>
where
    MdcPin: OutputPin<Error = E>,
    MdioPin: InputPin<Error = E> + OutputPin<Error = E>,
    Clk: CountDown + Periodic,
{
    type Error = E;
    fn read(&mut self, ctrl_bits: u16) -> Result<u16, Self::Error> {
        self.preamble()?;
        let [ctrl_a, ctrl_b] = ctrl_bits.to_be_bytes();
        self.write_u8(ctrl_a)?;
        self.write_bits(ctrl_b, 6)?;
        self.turnaround()?;
        self.read_u16()
    }
}

impl<MdioPin, MdcPin, Clk, E> Write for Mdio<MdioPin, MdcPin, Clk>
where
    MdcPin: OutputPin<Error = E>,
    MdioPin: InputPin<Error = E> + OutputPin<Error = E>,
    Clk: CountDown + Periodic,
{
    type Error = E;
    fn write(&mut self, ctrl_bits: u16, data_bits: u16) -> Result<(), Self::Error> {
        self.preamble()?;
        self.write_u16(ctrl_bits)?;
        self.write_u16(data_bits)
    }
}

/// Whether or not the bit at the given index is set.
///
/// Assumes a `bit_index` in the range `0..8`, where `0` is the most significant bit in the byte,
/// `7` is the least significant.
fn bit_is_set(byte: u8, bit_index: usize) -> bool {
    let out_bit = (byte >> (7 - bit_index)) & 0b1;
    out_bit == 1
}

/// Set the bit at the given index.
///
/// Assumes a `bit_index` in the range `0..8`, where `0` is the most significant bit in the byte,
/// `7` is the least significant.
fn set_bit(byte: &mut u8, bit_index: usize) {
    *byte |= 1 << (7 - bit_index);
}