Merge pull request #1746 from embassy-rs/enc28j60-v2
wip: enc28j60 driver.
This commit is contained in:
		
						commit
						03576b9e83
					
				
							
								
								
									
										18
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@ -6,16 +6,21 @@
 | 
			
		||||
  "rust-analyzer.check.allTargets": false,
 | 
			
		||||
  "rust-analyzer.check.noDefaultFeatures": true,
 | 
			
		||||
  "rust-analyzer.cargo.noDefaultFeatures": true,
 | 
			
		||||
  "rust-analyzer.cargo.target": "thumbv7m-none-eabi",
 | 
			
		||||
  "rust-analyzer.showUnlinkedFileNotification": false,
 | 
			
		||||
  // uncomment the target of your chip.
 | 
			
		||||
  //"rust-analyzer.cargo.target": "thumbv6m-none-eabi",
 | 
			
		||||
  //"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
 | 
			
		||||
  "rust-analyzer.cargo.target": "thumbv7em-none-eabi",
 | 
			
		||||
  //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
 | 
			
		||||
  "rust-analyzer.cargo.features": [
 | 
			
		||||
    ///"nightly",
 | 
			
		||||
    // Uncomment if the example has a "nightly" feature.
 | 
			
		||||
    "nightly",
 | 
			
		||||
  ],
 | 
			
		||||
  "rust-analyzer.linkedProjects": [
 | 
			
		||||
    // Declare for the target you wish to develop
 | 
			
		||||
    // "embassy-executor/Cargo.toml",
 | 
			
		||||
    // "embassy-sync/Cargo.toml",
 | 
			
		||||
    "examples/stm32wl/Cargo.toml",
 | 
			
		||||
    // Uncomment ONE line for the chip you want to work on.
 | 
			
		||||
    // This makes rust-analyzer work on the example crate and all its dependencies.
 | 
			
		||||
    "examples/nrf52840/Cargo.toml",
 | 
			
		||||
    // "examples/nrf52840-rtic/Cargo.toml",
 | 
			
		||||
    // "examples/nrf5340/Cargo.toml",
 | 
			
		||||
    // "examples/nrf-rtos-trace/Cargo.toml",
 | 
			
		||||
    // "examples/rp/Cargo.toml",
 | 
			
		||||
@ -41,5 +46,4 @@
 | 
			
		||||
    // "examples/stm32wl/Cargo.toml",
 | 
			
		||||
    // "examples/wasm/Cargo.toml",
 | 
			
		||||
  ],
 | 
			
		||||
  "rust-analyzer.showUnlinkedFileNotification": false,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								embassy-net-enc28j60/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								embassy-net-enc28j60/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "embassy-net-enc28j60"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
description = "embassy-net driver for the ENC28J60 ethernet chip"
 | 
			
		||||
keywords = ["embedded", "enc28j60", "embassy-net", "embedded-hal-async", "ethernet", "async"]
 | 
			
		||||
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
 | 
			
		||||
license = "MIT OR Apache-2.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
embedded-hal = { version = "1.0.0-alpha.11" }
 | 
			
		||||
embedded-hal-async = { version = "=0.2.0-alpha.2" }
 | 
			
		||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
 | 
			
		||||
embassy-time = { version = "0.1.2", path = "../embassy-time" }
 | 
			
		||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
 | 
			
		||||
 | 
			
		||||
defmt = { version = "0.3", optional = true }
 | 
			
		||||
log = { version = "0.4.14", optional = true }
 | 
			
		||||
 | 
			
		||||
[package.metadata.embassy_docs]
 | 
			
		||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/"
 | 
			
		||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/"
 | 
			
		||||
target = "thumbv7em-none-eabi"
 | 
			
		||||
							
								
								
									
										19
									
								
								embassy-net-enc28j60/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								embassy-net-enc28j60/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
# `embassy-net-enc28j60`
 | 
			
		||||
 | 
			
		||||
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the Microchip ENC28J60 Ethernet chip.
 | 
			
		||||
 | 
			
		||||
Based on [@japaric](https://github.com/japaric)'s [`enc28j60`](https://github.com/japaric/enc28j60) crate.
 | 
			
		||||
 | 
			
		||||
## Interoperability
 | 
			
		||||
 | 
			
		||||
This crate can run on any executor.
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
This work is licensed under either of
 | 
			
		||||
 | 
			
		||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
 | 
			
		||||
  http://www.apache.org/licenses/LICENSE-2.0)
 | 
			
		||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
 | 
			
		||||
 | 
			
		||||
at your option.
 | 
			
		||||
							
								
								
									
										69
									
								
								embassy-net-enc28j60/src/bank0.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								embassy-net-enc28j60/src/bank0.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,69 @@
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum Register {
 | 
			
		||||
    ERDPTL = 0x00,
 | 
			
		||||
    ERDPTH = 0x01,
 | 
			
		||||
    EWRPTL = 0x02,
 | 
			
		||||
    EWRPTH = 0x03,
 | 
			
		||||
    ETXSTL = 0x04,
 | 
			
		||||
    ETXSTH = 0x05,
 | 
			
		||||
    ETXNDL = 0x06,
 | 
			
		||||
    ETXNDH = 0x07,
 | 
			
		||||
    ERXSTL = 0x08,
 | 
			
		||||
    ERXSTH = 0x09,
 | 
			
		||||
    ERXNDL = 0x0a,
 | 
			
		||||
    ERXNDH = 0x0b,
 | 
			
		||||
    ERXRDPTL = 0x0c,
 | 
			
		||||
    ERXRDPTH = 0x0d,
 | 
			
		||||
    ERXWRPTL = 0x0e,
 | 
			
		||||
    ERXWRPTH = 0x0f,
 | 
			
		||||
    EDMASTL = 0x10,
 | 
			
		||||
    EDMASTH = 0x11,
 | 
			
		||||
    EDMANDL = 0x12,
 | 
			
		||||
    EDMANDH = 0x13,
 | 
			
		||||
    EDMADSTL = 0x14,
 | 
			
		||||
    EDMADSTH = 0x15,
 | 
			
		||||
    EDMACSL = 0x16,
 | 
			
		||||
    EDMACSH = 0x17,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Register {
 | 
			
		||||
    pub(crate) fn addr(&self) -> u8 {
 | 
			
		||||
        *self as u8
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn is_eth_register(&self) -> bool {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Register::ERDPTL => true,
 | 
			
		||||
            Register::ERDPTH => true,
 | 
			
		||||
            Register::EWRPTL => true,
 | 
			
		||||
            Register::EWRPTH => true,
 | 
			
		||||
            Register::ETXSTL => true,
 | 
			
		||||
            Register::ETXSTH => true,
 | 
			
		||||
            Register::ETXNDL => true,
 | 
			
		||||
            Register::ETXNDH => true,
 | 
			
		||||
            Register::ERXSTL => true,
 | 
			
		||||
            Register::ERXSTH => true,
 | 
			
		||||
            Register::ERXNDL => true,
 | 
			
		||||
            Register::ERXNDH => true,
 | 
			
		||||
            Register::ERXRDPTL => true,
 | 
			
		||||
            Register::ERXRDPTH => true,
 | 
			
		||||
            Register::ERXWRPTL => true,
 | 
			
		||||
            Register::ERXWRPTH => true,
 | 
			
		||||
            Register::EDMASTL => true,
 | 
			
		||||
            Register::EDMASTH => true,
 | 
			
		||||
            Register::EDMANDL => true,
 | 
			
		||||
            Register::EDMANDH => true,
 | 
			
		||||
            Register::EDMADSTL => true,
 | 
			
		||||
            Register::EDMADSTH => true,
 | 
			
		||||
            Register::EDMACSL => true,
 | 
			
		||||
            Register::EDMACSH => true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Into<super::Register> for Register {
 | 
			
		||||
    fn into(self) -> super::Register {
 | 
			
		||||
        super::Register::Bank0(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										84
									
								
								embassy-net-enc28j60/src/bank1.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								embassy-net-enc28j60/src/bank1.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum Register {
 | 
			
		||||
    EHT0 = 0x00,
 | 
			
		||||
    EHT1 = 0x01,
 | 
			
		||||
    EHT2 = 0x02,
 | 
			
		||||
    EHT3 = 0x03,
 | 
			
		||||
    EHT4 = 0x04,
 | 
			
		||||
    EHT5 = 0x05,
 | 
			
		||||
    EHT6 = 0x06,
 | 
			
		||||
    EHT7 = 0x07,
 | 
			
		||||
    EPMM0 = 0x08,
 | 
			
		||||
    EPMM1 = 0x09,
 | 
			
		||||
    EPMM2 = 0x0a,
 | 
			
		||||
    EPMM3 = 0x0b,
 | 
			
		||||
    EPMM4 = 0x0c,
 | 
			
		||||
    EPMM5 = 0x0d,
 | 
			
		||||
    EPMM6 = 0x0e,
 | 
			
		||||
    EPMM7 = 0x0f,
 | 
			
		||||
    EPMCSL = 0x10,
 | 
			
		||||
    EPMCSH = 0x11,
 | 
			
		||||
    EPMOL = 0x14,
 | 
			
		||||
    EPMOH = 0x15,
 | 
			
		||||
    ERXFCON = 0x18,
 | 
			
		||||
    EPKTCNT = 0x19,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Register {
 | 
			
		||||
    pub(crate) fn addr(&self) -> u8 {
 | 
			
		||||
        *self as u8
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn is_eth_register(&self) -> bool {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Register::EHT0 => true,
 | 
			
		||||
            Register::EHT1 => true,
 | 
			
		||||
            Register::EHT2 => true,
 | 
			
		||||
            Register::EHT3 => true,
 | 
			
		||||
            Register::EHT4 => true,
 | 
			
		||||
            Register::EHT5 => true,
 | 
			
		||||
            Register::EHT6 => true,
 | 
			
		||||
            Register::EHT7 => true,
 | 
			
		||||
            Register::EPMM0 => true,
 | 
			
		||||
            Register::EPMM1 => true,
 | 
			
		||||
            Register::EPMM2 => true,
 | 
			
		||||
            Register::EPMM3 => true,
 | 
			
		||||
            Register::EPMM4 => true,
 | 
			
		||||
            Register::EPMM5 => true,
 | 
			
		||||
            Register::EPMM6 => true,
 | 
			
		||||
            Register::EPMM7 => true,
 | 
			
		||||
            Register::EPMCSL => true,
 | 
			
		||||
            Register::EPMCSH => true,
 | 
			
		||||
            Register::EPMOL => true,
 | 
			
		||||
            Register::EPMOH => true,
 | 
			
		||||
            Register::ERXFCON => true,
 | 
			
		||||
            Register::EPKTCNT => true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Into<super::Register> for Register {
 | 
			
		||||
    fn into(self) -> super::Register {
 | 
			
		||||
        super::Register::Bank1(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
register!(ERXFCON, 0b1010_0001, u8, {
 | 
			
		||||
    #[doc = "Broadcast Filter Enable bit"]
 | 
			
		||||
    bcen @ 0,
 | 
			
		||||
    #[doc = "Multicast Filter Enable bit"]
 | 
			
		||||
    mcen @ 1,
 | 
			
		||||
    #[doc = "Hash Table Filter Enable bit"]
 | 
			
		||||
    hten @ 2,
 | 
			
		||||
    #[doc = "Magic Packet™ Filter Enable bit"]
 | 
			
		||||
    mpen @ 3,
 | 
			
		||||
    #[doc = "Pattern Match Filter Enable bit"]
 | 
			
		||||
    pmen @ 4,
 | 
			
		||||
    #[doc = "Post-Filter CRC Check Enable bit"]
 | 
			
		||||
    crcen @ 5,
 | 
			
		||||
    #[doc = "AND/OR Filter Select bit"]
 | 
			
		||||
    andor @ 6,
 | 
			
		||||
    #[doc = "Unicast Filter Enable bit"]
 | 
			
		||||
    ucen @ 7,
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										86
									
								
								embassy-net-enc28j60/src/bank2.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								embassy-net-enc28j60/src/bank2.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,86 @@
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum Register {
 | 
			
		||||
    MACON1 = 0x00,
 | 
			
		||||
    MACON3 = 0x02,
 | 
			
		||||
    MACON4 = 0x03,
 | 
			
		||||
    MABBIPG = 0x04,
 | 
			
		||||
    MAIPGL = 0x06,
 | 
			
		||||
    MAIPGH = 0x07,
 | 
			
		||||
    MACLCON1 = 0x08,
 | 
			
		||||
    MACLCON2 = 0x09,
 | 
			
		||||
    MAMXFLL = 0x0a,
 | 
			
		||||
    MAMXFLH = 0x0b,
 | 
			
		||||
    MICMD = 0x12,
 | 
			
		||||
    MIREGADR = 0x14,
 | 
			
		||||
    MIWRL = 0x16,
 | 
			
		||||
    MIWRH = 0x17,
 | 
			
		||||
    MIRDL = 0x18,
 | 
			
		||||
    MIRDH = 0x19,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Register {
 | 
			
		||||
    pub(crate) fn addr(&self) -> u8 {
 | 
			
		||||
        *self as u8
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn is_eth_register(&self) -> bool {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Register::MACON1 => false,
 | 
			
		||||
            Register::MACON3 => false,
 | 
			
		||||
            Register::MACON4 => false,
 | 
			
		||||
            Register::MABBIPG => false,
 | 
			
		||||
            Register::MAIPGL => false,
 | 
			
		||||
            Register::MAIPGH => false,
 | 
			
		||||
            Register::MACLCON1 => false,
 | 
			
		||||
            Register::MACLCON2 => false,
 | 
			
		||||
            Register::MAMXFLL => false,
 | 
			
		||||
            Register::MAMXFLH => false,
 | 
			
		||||
            Register::MICMD => false,
 | 
			
		||||
            Register::MIREGADR => false,
 | 
			
		||||
            Register::MIWRL => false,
 | 
			
		||||
            Register::MIWRH => false,
 | 
			
		||||
            Register::MIRDL => false,
 | 
			
		||||
            Register::MIRDH => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Into<super::Register> for Register {
 | 
			
		||||
    fn into(self) -> super::Register {
 | 
			
		||||
        super::Register::Bank2(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
register!(MACON1, 0, u8, {
 | 
			
		||||
    #[doc = "Enable packets to be received by the MAC"]
 | 
			
		||||
    marxen @ 0,
 | 
			
		||||
    #[doc = "Control frames will be discarded after being processed by the MAC"]
 | 
			
		||||
    passall @ 1,
 | 
			
		||||
    #[doc = "Inhibit transmissions when pause control frames are received"]
 | 
			
		||||
    rxpaus @ 2,
 | 
			
		||||
    #[doc = "Allow the MAC to transmit pause control frames"]
 | 
			
		||||
    txpaus @ 3,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
register!(MACON3, 0, u8, {
 | 
			
		||||
    #[doc = "MAC will operate in Full-Duplex mode"]
 | 
			
		||||
    fuldpx @ 0,
 | 
			
		||||
    #[doc = "The type/length field of transmitted and received frames will be checked"]
 | 
			
		||||
    frmlnen @ 1,
 | 
			
		||||
    #[doc = "Frames bigger than MAMXFL will be aborted when transmitted or received"]
 | 
			
		||||
    hfrmen @ 2,
 | 
			
		||||
    #[doc = "No proprietary header is present"]
 | 
			
		||||
    phdren @ 3,
 | 
			
		||||
    #[doc = "MAC will append a valid CRC to all frames transmitted regardless of PADCFG bit"]
 | 
			
		||||
    txcrcen @ 4,
 | 
			
		||||
    #[doc = "All short frames will be zero-padded to 64 bytes and a valid CRC will then be appended"]
 | 
			
		||||
    padcfg @ 5..7,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
register!(MICMD, 0, u8, {
 | 
			
		||||
    #[doc = "MII Read Enable bit"]
 | 
			
		||||
    miird @ 0,
 | 
			
		||||
    #[doc = "MII Scan Enable bit"]
 | 
			
		||||
    miiscan @ 1,
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										53
									
								
								embassy-net-enc28j60/src/bank3.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								embassy-net-enc28j60/src/bank3.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum Register {
 | 
			
		||||
    MAADR5 = 0x00,
 | 
			
		||||
    MAADR6 = 0x01,
 | 
			
		||||
    MAADR3 = 0x02,
 | 
			
		||||
    MAADR4 = 0x03,
 | 
			
		||||
    MAADR1 = 0x04,
 | 
			
		||||
    MAADR2 = 0x05,
 | 
			
		||||
    EBSTSD = 0x06,
 | 
			
		||||
    EBSTCON = 0x07,
 | 
			
		||||
    EBSTCSL = 0x08,
 | 
			
		||||
    EBSTCSH = 0x09,
 | 
			
		||||
    MISTAT = 0x0a,
 | 
			
		||||
    EREVID = 0x12,
 | 
			
		||||
    ECOCON = 0x15,
 | 
			
		||||
    EFLOCON = 0x17,
 | 
			
		||||
    EPAUSL = 0x18,
 | 
			
		||||
    EPAUSH = 0x19,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Register {
 | 
			
		||||
    pub(crate) fn addr(&self) -> u8 {
 | 
			
		||||
        *self as u8
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn is_eth_register(&self) -> bool {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Register::MAADR5 => false,
 | 
			
		||||
            Register::MAADR6 => false,
 | 
			
		||||
            Register::MAADR3 => false,
 | 
			
		||||
            Register::MAADR4 => false,
 | 
			
		||||
            Register::MAADR1 => false,
 | 
			
		||||
            Register::MAADR2 => false,
 | 
			
		||||
            Register::EBSTSD => true,
 | 
			
		||||
            Register::EBSTCON => true,
 | 
			
		||||
            Register::EBSTCSL => true,
 | 
			
		||||
            Register::EBSTCSH => true,
 | 
			
		||||
            Register::MISTAT => false,
 | 
			
		||||
            Register::EREVID => true,
 | 
			
		||||
            Register::ECOCON => true,
 | 
			
		||||
            Register::EFLOCON => true,
 | 
			
		||||
            Register::EPAUSL => true,
 | 
			
		||||
            Register::EPAUSH => true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Into<super::Register> for Register {
 | 
			
		||||
    fn into(self) -> super::Register {
 | 
			
		||||
        super::Register::Bank3(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										106
									
								
								embassy-net-enc28j60/src/common.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								embassy-net-enc28j60/src/common.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,106 @@
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum Register {
 | 
			
		||||
    ECON1 = 0x1f,
 | 
			
		||||
    ECON2 = 0x1e,
 | 
			
		||||
    EIE = 0x1b,
 | 
			
		||||
    EIR = 0x1c,
 | 
			
		||||
    ESTAT = 0x1d,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Register {
 | 
			
		||||
    pub(crate) fn addr(&self) -> u8 {
 | 
			
		||||
        *self as u8
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn is_eth_register(&self) -> bool {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Register::ECON1 => true,
 | 
			
		||||
            Register::ECON2 => true,
 | 
			
		||||
            Register::EIE => true,
 | 
			
		||||
            Register::EIR => true,
 | 
			
		||||
            Register::ESTAT => true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Into<super::Register> for Register {
 | 
			
		||||
    fn into(self) -> super::Register {
 | 
			
		||||
        super::Register::Common(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
register!(EIE, 0, u8, {
 | 
			
		||||
    #[doc = "Receive Error Interrupt Enable bit"]
 | 
			
		||||
    rxerie @ 0,
 | 
			
		||||
    #[doc = "Transmit Error Interrupt Enable bit"]
 | 
			
		||||
    txerie @ 1,
 | 
			
		||||
    #[doc = "Transmit Enable bit"]
 | 
			
		||||
    txie @ 3,
 | 
			
		||||
    #[doc = "Link Status Change Interrupt Enable bit"]
 | 
			
		||||
    linkie @ 4,
 | 
			
		||||
    #[doc = "DMA Interrupt Enable bit"]
 | 
			
		||||
    dmaie @ 5,
 | 
			
		||||
    #[doc = "Receive Packet Pending Interrupt Enable bit"]
 | 
			
		||||
    pktie @ 6,
 | 
			
		||||
    #[doc = "Global INT Interrupt Enable bit"]
 | 
			
		||||
    intie @ 7,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
register!(EIR, 0, u8, {
 | 
			
		||||
    #[doc = "Receive Error Interrupt Flag bit"]
 | 
			
		||||
    rxerif @ 0,
 | 
			
		||||
    #[doc = "Transmit Error Interrupt Flag bit"]
 | 
			
		||||
    txerif @ 1,
 | 
			
		||||
    #[doc = "Transmit Interrupt Flag bit"]
 | 
			
		||||
    txif @ 3,
 | 
			
		||||
    #[doc = "Link Change Interrupt Flag bit"]
 | 
			
		||||
    linkif @ 4,
 | 
			
		||||
    #[doc = "DMA Interrupt Flag bit"]
 | 
			
		||||
    dmaif @ 5,
 | 
			
		||||
    #[doc = "Receive Packet Pending Interrupt Flag bit"]
 | 
			
		||||
    pktif @ 6,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
register!(ESTAT, 0, u8, {
 | 
			
		||||
    #[doc = "Clock Ready bit"]
 | 
			
		||||
    clkrdy @ 0,
 | 
			
		||||
    #[doc = "Transmit Abort Error bit"]
 | 
			
		||||
    txabrt @ 1,
 | 
			
		||||
    #[doc = "Receive Busy bit"]
 | 
			
		||||
    rxbusy @ 2,
 | 
			
		||||
    #[doc = "Late Collision Error bit"]
 | 
			
		||||
    latecol @ 4,
 | 
			
		||||
    #[doc = "Ethernet Buffer Error Status bit"]
 | 
			
		||||
    bufer @ 6,
 | 
			
		||||
    #[doc = "INT Interrupt Flag bit"]
 | 
			
		||||
    int @ 7,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
register!(ECON2, 0b1000_0000, u8, {
 | 
			
		||||
    #[doc = "Voltage Regulator Power Save Enable bit"]
 | 
			
		||||
    vrps @ 3,
 | 
			
		||||
    #[doc = "Power Save Enable bit"]
 | 
			
		||||
    pwrsv @ 5,
 | 
			
		||||
    #[doc = "Packet Decrement bit"]
 | 
			
		||||
    pktdec @ 6,
 | 
			
		||||
    #[doc = "Automatic Buffer Pointer Increment Enable bit"]
 | 
			
		||||
    autoinc @ 7,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
register!(ECON1, 0, u8, {
 | 
			
		||||
    #[doc = "Bank Select bits"]
 | 
			
		||||
    bsel @ 0..1,
 | 
			
		||||
    #[doc = "Receive Enable bi"]
 | 
			
		||||
    rxen @ 2,
 | 
			
		||||
    #[doc = "Transmit Request to Send bit"]
 | 
			
		||||
    txrts @ 3,
 | 
			
		||||
    #[doc = "DMA Checksum Enable bit"]
 | 
			
		||||
    csumen @ 4,
 | 
			
		||||
    #[doc = "DMA Start and Busy Status bit"]
 | 
			
		||||
    dmast @ 5,
 | 
			
		||||
    #[doc = "Receive Logic Reset bit"]
 | 
			
		||||
    rxrst @ 6,
 | 
			
		||||
    #[doc = "Transmit Logic Reset bit"]
 | 
			
		||||
    txrst @ 7,
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										225
									
								
								embassy-net-enc28j60/src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								embassy-net-enc28j60/src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,225 @@
 | 
			
		||||
#![macro_use]
 | 
			
		||||
#![allow(unused_macros)]
 | 
			
		||||
 | 
			
		||||
#[cfg(all(feature = "defmt", feature = "log"))]
 | 
			
		||||
compile_error!("You may not enable both `defmt` and `log` features.");
 | 
			
		||||
 | 
			
		||||
macro_rules! assert {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::assert!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::assert!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! assert_eq {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::assert_eq!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::assert_eq!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! assert_ne {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::assert_ne!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::assert_ne!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! debug_assert {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::debug_assert!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::debug_assert!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! debug_assert_eq {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::debug_assert_eq!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::debug_assert_eq!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! debug_assert_ne {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::debug_assert_ne!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::debug_assert_ne!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! todo {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::todo!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::todo!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! unreachable {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::unreachable!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::unreachable!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! panic {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::panic!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::panic!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! trace {
 | 
			
		||||
    ($s:literal $(, $x:expr)* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "log")]
 | 
			
		||||
            ::log::trace!($s $(, $x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::trace!($s $(, $x)*);
 | 
			
		||||
            #[cfg(not(any(feature = "log", feature="defmt")))]
 | 
			
		||||
            let _ = ($( & $x ),*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! debug {
 | 
			
		||||
    ($s:literal $(, $x:expr)* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "log")]
 | 
			
		||||
            ::log::debug!($s $(, $x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::debug!($s $(, $x)*);
 | 
			
		||||
            #[cfg(not(any(feature = "log", feature="defmt")))]
 | 
			
		||||
            let _ = ($( & $x ),*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! info {
 | 
			
		||||
    ($s:literal $(, $x:expr)* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "log")]
 | 
			
		||||
            ::log::info!($s $(, $x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::info!($s $(, $x)*);
 | 
			
		||||
            #[cfg(not(any(feature = "log", feature="defmt")))]
 | 
			
		||||
            let _ = ($( & $x ),*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! warn {
 | 
			
		||||
    ($s:literal $(, $x:expr)* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "log")]
 | 
			
		||||
            ::log::warn!($s $(, $x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::warn!($s $(, $x)*);
 | 
			
		||||
            #[cfg(not(any(feature = "log", feature="defmt")))]
 | 
			
		||||
            let _ = ($( & $x ),*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! error {
 | 
			
		||||
    ($s:literal $(, $x:expr)* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "log")]
 | 
			
		||||
            ::log::error!($s $(, $x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::error!($s $(, $x)*);
 | 
			
		||||
            #[cfg(not(any(feature = "log", feature="defmt")))]
 | 
			
		||||
            let _ = ($( & $x ),*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "defmt")]
 | 
			
		||||
macro_rules! unwrap {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        ::defmt::unwrap!($($x)*)
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(not(feature = "defmt"))]
 | 
			
		||||
macro_rules! unwrap {
 | 
			
		||||
    ($arg:expr) => {
 | 
			
		||||
        match $crate::fmt::Try::into_result($arg) {
 | 
			
		||||
            ::core::result::Result::Ok(t) => t,
 | 
			
		||||
            ::core::result::Result::Err(e) => {
 | 
			
		||||
                ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($arg:expr, $($msg:expr),+ $(,)? ) => {
 | 
			
		||||
        match $crate::fmt::Try::into_result($arg) {
 | 
			
		||||
            ::core::result::Result::Ok(t) => t,
 | 
			
		||||
            ::core::result::Result::Err(e) => {
 | 
			
		||||
                ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
			
		||||
pub struct NoneError;
 | 
			
		||||
 | 
			
		||||
pub trait Try {
 | 
			
		||||
    type Ok;
 | 
			
		||||
    type Error;
 | 
			
		||||
    fn into_result(self) -> Result<Self::Ok, Self::Error>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> Try for Option<T> {
 | 
			
		||||
    type Ok = T;
 | 
			
		||||
    type Error = NoneError;
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn into_result(self) -> Result<T, NoneError> {
 | 
			
		||||
        self.ok_or(NoneError)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T, E> Try for Result<T, E> {
 | 
			
		||||
    type Ok = T;
 | 
			
		||||
    type Error = E;
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn into_result(self) -> Self {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								embassy-net-enc28j60/src/header.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								embassy-net-enc28j60/src/header.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
register!(RxStatus, 0, u32, {
 | 
			
		||||
    #[doc = "Indicates length of the received frame"]
 | 
			
		||||
    byte_count @ 0..15,
 | 
			
		||||
    #[doc = "Indicates a packet over 50,000 bit times occurred or that a packet was dropped since the last receive"]
 | 
			
		||||
    long_event @ 16,
 | 
			
		||||
    #[doc = "Indicates that at some time since the last receive, a carrier event was detected"]
 | 
			
		||||
    carrier_event @ 18,
 | 
			
		||||
    #[doc = "Indicates that frame CRC field value does not match the CRC calculated by the MAC"]
 | 
			
		||||
    crc_error @ 20,
 | 
			
		||||
    #[doc = "Indicates that frame length field value in the packet does not match the actual data byte length and specifies a valid length"]
 | 
			
		||||
    length_check_error @ 21,
 | 
			
		||||
    #[doc = "Indicates that frame type/length field was larger than 1500 bytes (type field)"]
 | 
			
		||||
    length_out_of_range @ 22,
 | 
			
		||||
    #[doc = "Indicates that at the packet had a valid CRC and no symbol errors"]
 | 
			
		||||
    received_ok @ 23,
 | 
			
		||||
    #[doc = "Indicates packet received had a valid Multicast address"]
 | 
			
		||||
    multicast @ 24,
 | 
			
		||||
    #[doc = "Indicates packet received had a valid Broadcast address."]
 | 
			
		||||
    broadcast @ 25,
 | 
			
		||||
    #[doc = "Indicates that after the end of this packet, an additional 1 to 7 bits were received"]
 | 
			
		||||
    dribble_nibble @ 26,
 | 
			
		||||
    #[doc = "Current frame was recognized as a control frame for having a valid type/length designating it as a control frame"]
 | 
			
		||||
    receive_control_frame @ 27,
 | 
			
		||||
    #[doc = "Current frame was recognized as a control frame containing a valid pause frame opcode and a valid destination address"]
 | 
			
		||||
    receive_pause_control_frame @ 28,
 | 
			
		||||
    #[doc = "Current frame was recognized as a control frame but it contained an unknown opcode"]
 | 
			
		||||
    receive_unknown_opcode @ 29,
 | 
			
		||||
    #[doc = "Current frame was recognized as a VLAN tagged frame"]
 | 
			
		||||
    receive_vlan_type_detected @ 30,
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										707
									
								
								embassy-net-enc28j60/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										707
									
								
								embassy-net-enc28j60/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,707 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![doc = include_str!("../README.md")]
 | 
			
		||||
#![warn(missing_docs)]
 | 
			
		||||
 | 
			
		||||
// must go first.
 | 
			
		||||
mod fmt;
 | 
			
		||||
 | 
			
		||||
#[macro_use]
 | 
			
		||||
mod macros;
 | 
			
		||||
mod bank0;
 | 
			
		||||
mod bank1;
 | 
			
		||||
mod bank2;
 | 
			
		||||
mod bank3;
 | 
			
		||||
mod common;
 | 
			
		||||
mod header;
 | 
			
		||||
mod phy;
 | 
			
		||||
mod traits;
 | 
			
		||||
 | 
			
		||||
use core::cmp;
 | 
			
		||||
use core::convert::TryInto;
 | 
			
		||||
 | 
			
		||||
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium};
 | 
			
		||||
use embassy_time::Duration;
 | 
			
		||||
use embedded_hal::digital::OutputPin;
 | 
			
		||||
use embedded_hal::spi::{Operation, SpiDevice};
 | 
			
		||||
use traits::U16Ext;
 | 
			
		||||
 | 
			
		||||
// Total buffer size (see section 3.2)
 | 
			
		||||
const BUF_SZ: u16 = 8 * 1024;
 | 
			
		||||
 | 
			
		||||
// Maximum frame length
 | 
			
		||||
const MAX_FRAME_LENGTH: u16 = 1518; // value recommended in the data sheet
 | 
			
		||||
 | 
			
		||||
// Size of the Frame check sequence (32-bit CRC)
 | 
			
		||||
const CRC_SZ: u16 = 4;
 | 
			
		||||
 | 
			
		||||
// define the boundaries of the TX and RX buffers
 | 
			
		||||
// to workaround errata #5 we do the opposite of what section 6.1 of the data sheet
 | 
			
		||||
// says: we place the RX buffer at address 0 and the TX buffer after it
 | 
			
		||||
const RXST: u16 = 0x0000;
 | 
			
		||||
const RXND: u16 = 0x19ff;
 | 
			
		||||
const TXST: u16 = 0x1a00;
 | 
			
		||||
const _TXND: u16 = 0x1fff;
 | 
			
		||||
 | 
			
		||||
const MTU: usize = 1514; // 1500 IP + 14 ethernet header
 | 
			
		||||
 | 
			
		||||
/// ENC28J60 embassy-net driver
 | 
			
		||||
pub struct Enc28j60<S, O> {
 | 
			
		||||
    mac_addr: [u8; 6],
 | 
			
		||||
 | 
			
		||||
    spi: S,
 | 
			
		||||
    rst: Option<O>,
 | 
			
		||||
 | 
			
		||||
    bank: Bank,
 | 
			
		||||
 | 
			
		||||
    // address of the next packet in buffer memory
 | 
			
		||||
    next_packet: u16,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<S, O> Enc28j60<S, O>
 | 
			
		||||
where
 | 
			
		||||
    S: SpiDevice,
 | 
			
		||||
    O: OutputPin,
 | 
			
		||||
{
 | 
			
		||||
    /// Create a new ENC28J60 driver instance.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The RST pin is optional. If None, reset will be done with a SPI
 | 
			
		||||
    /// soft reset command, instead of via the RST pin.
 | 
			
		||||
    pub fn new(spi: S, rst: Option<O>, mac_addr: [u8; 6]) -> Self {
 | 
			
		||||
        let mut res = Self {
 | 
			
		||||
            mac_addr,
 | 
			
		||||
            spi,
 | 
			
		||||
            rst,
 | 
			
		||||
 | 
			
		||||
            bank: Bank::Bank0,
 | 
			
		||||
            next_packet: RXST,
 | 
			
		||||
        };
 | 
			
		||||
        res.init();
 | 
			
		||||
        res
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn init(&mut self) {
 | 
			
		||||
        if let Some(rst) = &mut self.rst {
 | 
			
		||||
            rst.set_low().unwrap();
 | 
			
		||||
            embassy_time::block_for(Duration::from_millis(5));
 | 
			
		||||
            rst.set_high().unwrap();
 | 
			
		||||
            embassy_time::block_for(Duration::from_millis(5));
 | 
			
		||||
        } else {
 | 
			
		||||
            embassy_time::block_for(Duration::from_millis(5));
 | 
			
		||||
            self.soft_reset();
 | 
			
		||||
            embassy_time::block_for(Duration::from_millis(5));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        debug!(
 | 
			
		||||
            "enc28j60: erevid {=u8:x}",
 | 
			
		||||
            self.read_control_register(bank3::Register::EREVID)
 | 
			
		||||
        );
 | 
			
		||||
        debug!("enc28j60: waiting for clk");
 | 
			
		||||
        while common::ESTAT(self.read_control_register(common::Register::ESTAT)).clkrdy() == 0 {}
 | 
			
		||||
        debug!("enc28j60: clk ok");
 | 
			
		||||
 | 
			
		||||
        if self.read_control_register(bank3::Register::EREVID) == 0 {
 | 
			
		||||
            panic!("ErevidIsZero");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // disable CLKOUT output
 | 
			
		||||
        self.write_control_register(bank3::Register::ECOCON, 0);
 | 
			
		||||
 | 
			
		||||
        // RX start
 | 
			
		||||
        // "It is recommended that the ERXST Pointer be programmed with an even address"
 | 
			
		||||
        self.write_control_register(bank0::Register::ERXSTL, RXST.low());
 | 
			
		||||
        self.write_control_register(bank0::Register::ERXSTH, RXST.high());
 | 
			
		||||
 | 
			
		||||
        // RX read pointer
 | 
			
		||||
        // NOTE Errata #14 so we are using an *odd* address here instead of ERXST
 | 
			
		||||
        self.write_control_register(bank0::Register::ERXRDPTL, RXND.low());
 | 
			
		||||
        self.write_control_register(bank0::Register::ERXRDPTH, RXND.high());
 | 
			
		||||
 | 
			
		||||
        // RX end
 | 
			
		||||
        self.write_control_register(bank0::Register::ERXNDL, RXND.low());
 | 
			
		||||
        self.write_control_register(bank0::Register::ERXNDH, RXND.high());
 | 
			
		||||
 | 
			
		||||
        // TX start
 | 
			
		||||
        // "It is recommended that an even address be used for ETXST"
 | 
			
		||||
        debug_assert_eq!(TXST % 2, 0);
 | 
			
		||||
        self.write_control_register(bank0::Register::ETXSTL, TXST.low());
 | 
			
		||||
        self.write_control_register(bank0::Register::ETXSTH, TXST.high());
 | 
			
		||||
 | 
			
		||||
        // TX end is set in `transmit`
 | 
			
		||||
 | 
			
		||||
        // MAC initialization (see section 6.5)
 | 
			
		||||
        // 1. Set the MARXEN bit in MACON1 to enable the MAC to receive frames.
 | 
			
		||||
        self.write_control_register(
 | 
			
		||||
            bank2::Register::MACON1,
 | 
			
		||||
            bank2::MACON1::default().marxen(1).passall(0).rxpaus(1).txpaus(1).bits(),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // 2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3.
 | 
			
		||||
        self.write_control_register(
 | 
			
		||||
            bank2::Register::MACON3,
 | 
			
		||||
            bank2::MACON3::default().frmlnen(1).txcrcen(1).padcfg(0b001).bits(),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // 4. Program the MAMXFL registers with the maximum frame length to be permitted to be
 | 
			
		||||
        // received or transmitted
 | 
			
		||||
        self.write_control_register(bank2::Register::MAMXFLL, MAX_FRAME_LENGTH.low());
 | 
			
		||||
        self.write_control_register(bank2::Register::MAMXFLH, MAX_FRAME_LENGTH.high());
 | 
			
		||||
 | 
			
		||||
        // 5. Configure the Back-to-Back Inter-Packet Gap register, MABBIPG.
 | 
			
		||||
        // Use recommended value of 0x12
 | 
			
		||||
        self.write_control_register(bank2::Register::MABBIPG, 0x12);
 | 
			
		||||
 | 
			
		||||
        // 6. Configure the Non-Back-to-Back Inter-Packet Gap register low byte, MAIPGL.
 | 
			
		||||
        // Use recommended value of 0x12
 | 
			
		||||
        self.write_control_register(bank2::Register::MAIPGL, 0x12);
 | 
			
		||||
        self.write_control_register(bank2::Register::MAIPGH, 0x0c);
 | 
			
		||||
 | 
			
		||||
        // 9. Program the local MAC address into the MAADR1:MAADR6 registers
 | 
			
		||||
        self.write_control_register(bank3::Register::MAADR1, self.mac_addr[0]);
 | 
			
		||||
        self.write_control_register(bank3::Register::MAADR2, self.mac_addr[1]);
 | 
			
		||||
        self.write_control_register(bank3::Register::MAADR3, self.mac_addr[2]);
 | 
			
		||||
        self.write_control_register(bank3::Register::MAADR4, self.mac_addr[3]);
 | 
			
		||||
        self.write_control_register(bank3::Register::MAADR5, self.mac_addr[4]);
 | 
			
		||||
        self.write_control_register(bank3::Register::MAADR6, self.mac_addr[5]);
 | 
			
		||||
 | 
			
		||||
        // Set the PHCON2.HDLDIS bit to prevent automatic loopback of the data which is transmitted
 | 
			
		||||
        self.write_phy_register(phy::Register::PHCON2, phy::PHCON2::default().hdldis(1).bits());
 | 
			
		||||
 | 
			
		||||
        // Globally enable interrupts
 | 
			
		||||
        //self.bit_field_set(common::Register::EIE, common::EIE::mask().intie());
 | 
			
		||||
 | 
			
		||||
        // Set the per packet control byte; we'll always use the value 0
 | 
			
		||||
        self.write_buffer_memory(Some(TXST), &mut [0]);
 | 
			
		||||
 | 
			
		||||
        // decrease the packet count to 0
 | 
			
		||||
        while self.read_control_register(bank1::Register::EPKTCNT) != 0 {
 | 
			
		||||
            self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Enable reception
 | 
			
		||||
        self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Flushes the transmit buffer, ensuring all pending transmissions have completed
 | 
			
		||||
    /// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always
 | 
			
		||||
    /// return `None` on subsequent invocations
 | 
			
		||||
    pub fn receive<'a>(&mut self, buf: &'a mut [u8]) -> Option<&'a mut [u8]> {
 | 
			
		||||
        if self.pending_packets() == 0 {
 | 
			
		||||
            // Errata #6: we can't rely on PKTIF so we check PKTCNT
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let curr_packet = self.next_packet;
 | 
			
		||||
 | 
			
		||||
        // read out the first 6 bytes
 | 
			
		||||
        let mut temp_buf = [0; 6];
 | 
			
		||||
        self.read_buffer_memory(Some(curr_packet), &mut temp_buf);
 | 
			
		||||
 | 
			
		||||
        // next packet pointer
 | 
			
		||||
        let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]);
 | 
			
		||||
        if next_packet > RXND {
 | 
			
		||||
            panic!("CorruptRxBuffer");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // status vector
 | 
			
		||||
        let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap()));
 | 
			
		||||
        let len = status.byte_count() as u16 - CRC_SZ;
 | 
			
		||||
 | 
			
		||||
        if len > RXND {
 | 
			
		||||
            panic!("CorruptRxBuffer 2");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.read_buffer_memory(None, &mut buf[..len as usize]);
 | 
			
		||||
 | 
			
		||||
        // update ERXRDPT
 | 
			
		||||
        // due to Errata #14 we must write an odd address to ERXRDPT
 | 
			
		||||
        // we know that ERXST = 0, that ERXND is odd and that next_packet is even
 | 
			
		||||
        let rxrdpt = if self.next_packet < 1 || self.next_packet > RXND + 1 {
 | 
			
		||||
            RXND
 | 
			
		||||
        } else {
 | 
			
		||||
            self.next_packet - 1
 | 
			
		||||
        };
 | 
			
		||||
        // "To move ERXRDPT, the host controller must write to ERXRDPTL first."
 | 
			
		||||
        self.write_control_register(bank0::Register::ERXRDPTL, rxrdpt.low());
 | 
			
		||||
        self.write_control_register(bank0::Register::ERXRDPTH, rxrdpt.high());
 | 
			
		||||
 | 
			
		||||
        // decrease the packet count
 | 
			
		||||
        self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
 | 
			
		||||
 | 
			
		||||
        self.next_packet = next_packet;
 | 
			
		||||
 | 
			
		||||
        Some(&mut buf[..len as usize])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn wait_tx_ready(&mut self) {
 | 
			
		||||
        for _ in 0u32..10000 {
 | 
			
		||||
            if common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 0 {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // work around errata #12 by resetting the transmit logic before every new
 | 
			
		||||
        // transmission
 | 
			
		||||
        self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrst());
 | 
			
		||||
        self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().txrst());
 | 
			
		||||
        //self.bit_field_clear(common::Register::EIR, {
 | 
			
		||||
        //    let mask = common::EIR::mask();
 | 
			
		||||
        //    mask.txerif() | mask.txif()
 | 
			
		||||
        //});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Starts the transmission of `bytes`
 | 
			
		||||
    ///
 | 
			
		||||
    /// It's up to the caller to ensure that `bytes` is a valid Ethernet frame. The interface will
 | 
			
		||||
    /// take care of appending a (4 byte) CRC to the frame and of padding the frame to the minimum
 | 
			
		||||
    /// size allowed by the Ethernet specification (64 bytes, or 46 bytes of payload).
 | 
			
		||||
    ///
 | 
			
		||||
    /// NOTE This method will flush any previous transmission that's in progress
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Panics
 | 
			
		||||
    ///
 | 
			
		||||
    /// If `bytes` length is greater than 1514, the maximum frame length allowed by the interface,
 | 
			
		||||
    /// or greater than the transmit buffer
 | 
			
		||||
    pub fn transmit(&mut self, bytes: &[u8]) {
 | 
			
		||||
        assert!(bytes.len() <= self.mtu() as usize);
 | 
			
		||||
 | 
			
		||||
        self.wait_tx_ready();
 | 
			
		||||
 | 
			
		||||
        // NOTE the plus one is to not overwrite the per packet control byte
 | 
			
		||||
        let wrpt = TXST + 1;
 | 
			
		||||
 | 
			
		||||
        // 1. ETXST was set during initialization
 | 
			
		||||
 | 
			
		||||
        // 2. write the frame to the IC memory
 | 
			
		||||
        self.write_buffer_memory(Some(wrpt), bytes);
 | 
			
		||||
 | 
			
		||||
        let txnd = wrpt + bytes.len() as u16 - 1;
 | 
			
		||||
 | 
			
		||||
        // 3. Set the end address of the transmit buffer
 | 
			
		||||
        self.write_control_register(bank0::Register::ETXNDL, txnd.low());
 | 
			
		||||
        self.write_control_register(bank0::Register::ETXNDH, txnd.high());
 | 
			
		||||
 | 
			
		||||
        // 4. reset interrupt flag
 | 
			
		||||
        //self.bit_field_clear(common::Register::EIR, common::EIR::mask().txif());
 | 
			
		||||
 | 
			
		||||
        // 5. start transmission
 | 
			
		||||
        self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrts());
 | 
			
		||||
 | 
			
		||||
        // Wait until transmission finishes
 | 
			
		||||
        //while common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 1 {}
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        // read the transmit status vector
 | 
			
		||||
        let mut tx_stat = [0; 7];
 | 
			
		||||
        self.read_buffer_memory(None, &mut tx_stat);
 | 
			
		||||
 | 
			
		||||
        let stat = common::ESTAT(self.read_control_register(common::Register::ESTAT));
 | 
			
		||||
 | 
			
		||||
        if stat.txabrt() == 1 {
 | 
			
		||||
            // work around errata #12 by reading the transmit status vector
 | 
			
		||||
            if stat.latecol() == 1 || (tx_stat[2] & (1 << 5)) != 0 {
 | 
			
		||||
                panic!("LateCollision")
 | 
			
		||||
            } else {
 | 
			
		||||
                panic!("TransmitAbort")
 | 
			
		||||
            }
 | 
			
		||||
        }*/
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get whether the link is up
 | 
			
		||||
    pub fn is_link_up(&mut self) -> bool {
 | 
			
		||||
        let bits = self.read_phy_register(phy::Register::PHSTAT2);
 | 
			
		||||
        phy::PHSTAT2(bits).lstat() == 1
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the interface Maximum Transmission Unit (MTU)
 | 
			
		||||
    ///
 | 
			
		||||
    /// The value returned by this function will never exceed 1514 bytes. The actual value depends
 | 
			
		||||
    /// on the memory assigned to the transmission buffer when initializing the device
 | 
			
		||||
    pub fn mtu(&self) -> u16 {
 | 
			
		||||
        cmp::min(BUF_SZ - RXND - 1, MAX_FRAME_LENGTH - CRC_SZ)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Miscellaneous */
 | 
			
		||||
    /// Returns the number of packets that have been received but have not been processed yet
 | 
			
		||||
    pub fn pending_packets(&mut self) -> u8 {
 | 
			
		||||
        self.read_control_register(bank1::Register::EPKTCNT)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Adjusts the receive filter to *accept* these packet types
 | 
			
		||||
    pub fn accept(&mut self, packets: &[Packet]) {
 | 
			
		||||
        let mask = bank1::ERXFCON::mask();
 | 
			
		||||
        let mut val = 0;
 | 
			
		||||
        for packet in packets {
 | 
			
		||||
            match packet {
 | 
			
		||||
                Packet::Broadcast => val |= mask.bcen(),
 | 
			
		||||
                Packet::Multicast => val |= mask.mcen(),
 | 
			
		||||
                Packet::Unicast => val |= mask.ucen(),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.bit_field_set(bank1::Register::ERXFCON, val)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Adjusts the receive filter to *ignore* these packet types
 | 
			
		||||
    pub fn ignore(&mut self, packets: &[Packet]) {
 | 
			
		||||
        let mask = bank1::ERXFCON::mask();
 | 
			
		||||
        let mut val = 0;
 | 
			
		||||
        for packet in packets {
 | 
			
		||||
            match packet {
 | 
			
		||||
                Packet::Broadcast => val |= mask.bcen(),
 | 
			
		||||
                Packet::Multicast => val |= mask.mcen(),
 | 
			
		||||
                Packet::Unicast => val |= mask.ucen(),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.bit_field_clear(bank1::Register::ERXFCON, val)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Private */
 | 
			
		||||
    /* Read */
 | 
			
		||||
    fn read_control_register<R>(&mut self, register: R) -> u8
 | 
			
		||||
    where
 | 
			
		||||
        R: Into<Register>,
 | 
			
		||||
    {
 | 
			
		||||
        self._read_control_register(register.into())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn _read_control_register(&mut self, register: Register) -> u8 {
 | 
			
		||||
        self.change_bank(register);
 | 
			
		||||
 | 
			
		||||
        if register.is_eth_register() {
 | 
			
		||||
            let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0];
 | 
			
		||||
            self.spi.transfer_in_place(&mut buffer).unwrap();
 | 
			
		||||
            buffer[1]
 | 
			
		||||
        } else {
 | 
			
		||||
            // MAC, MII regs need a dummy byte.
 | 
			
		||||
            let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0, 0];
 | 
			
		||||
            self.spi.transfer_in_place(&mut buffer).unwrap();
 | 
			
		||||
            buffer[2]
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn read_phy_register(&mut self, register: phy::Register) -> u16 {
 | 
			
		||||
        embassy_time::block_for(Duration::from_millis(1));
 | 
			
		||||
 | 
			
		||||
        // set PHY register address
 | 
			
		||||
        self.write_control_register(bank2::Register::MIREGADR, register.addr());
 | 
			
		||||
 | 
			
		||||
        // start read operation
 | 
			
		||||
        self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(1).bits());
 | 
			
		||||
 | 
			
		||||
        // wait until the read operation finishes
 | 
			
		||||
        while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
 | 
			
		||||
 | 
			
		||||
        self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits());
 | 
			
		||||
 | 
			
		||||
        let l = self.read_control_register(bank2::Register::MIRDL);
 | 
			
		||||
        let h = self.read_control_register(bank2::Register::MIRDH);
 | 
			
		||||
        (l as u16) | (h as u16) << 8
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Write */
 | 
			
		||||
    fn _write_control_register(&mut self, register: Register, value: u8) {
 | 
			
		||||
        self.change_bank(register);
 | 
			
		||||
 | 
			
		||||
        let buffer = [Instruction::WCR.opcode() | register.addr(), value];
 | 
			
		||||
        self.spi.write(&buffer).unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn write_control_register<R>(&mut self, register: R, value: u8)
 | 
			
		||||
    where
 | 
			
		||||
        R: Into<Register>,
 | 
			
		||||
    {
 | 
			
		||||
        self._write_control_register(register.into(), value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn write_phy_register(&mut self, register: phy::Register, value: u16) {
 | 
			
		||||
        // set PHY register address
 | 
			
		||||
        self.write_control_register(bank2::Register::MIREGADR, register.addr());
 | 
			
		||||
 | 
			
		||||
        self.write_control_register(bank2::Register::MIWRL, (value & 0xff) as u8);
 | 
			
		||||
        // this starts the write operation
 | 
			
		||||
        self.write_control_register(bank2::Register::MIWRH, (value >> 8) as u8);
 | 
			
		||||
 | 
			
		||||
        // wait until the write operation finishes
 | 
			
		||||
        while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* RMW */
 | 
			
		||||
    fn modify_control_register<R, F>(&mut self, register: R, f: F)
 | 
			
		||||
    where
 | 
			
		||||
        F: FnOnce(u8) -> u8,
 | 
			
		||||
        R: Into<Register>,
 | 
			
		||||
    {
 | 
			
		||||
        self._modify_control_register(register.into(), f)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn _modify_control_register<F>(&mut self, register: Register, f: F)
 | 
			
		||||
    where
 | 
			
		||||
        F: FnOnce(u8) -> u8,
 | 
			
		||||
    {
 | 
			
		||||
        let r = self._read_control_register(register);
 | 
			
		||||
        self._write_control_register(register, f(r))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Auxiliary */
 | 
			
		||||
    fn change_bank(&mut self, register: Register) {
 | 
			
		||||
        let bank = register.bank();
 | 
			
		||||
 | 
			
		||||
        if let Some(bank) = bank {
 | 
			
		||||
            if self.bank == bank {
 | 
			
		||||
                // already on the register bank
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // change bank
 | 
			
		||||
            self.bank = bank;
 | 
			
		||||
            match bank {
 | 
			
		||||
                Bank::Bank0 => self.bit_field_clear(common::Register::ECON1, 0b11),
 | 
			
		||||
                Bank::Bank1 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b01),
 | 
			
		||||
                Bank::Bank2 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b10),
 | 
			
		||||
                Bank::Bank3 => self.bit_field_set(common::Register::ECON1, 0b11),
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // common register
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Primitive operations */
 | 
			
		||||
    fn bit_field_clear<R>(&mut self, register: R, mask: u8)
 | 
			
		||||
    where
 | 
			
		||||
        R: Into<Register>,
 | 
			
		||||
    {
 | 
			
		||||
        self._bit_field_clear(register.into(), mask)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn _bit_field_clear(&mut self, register: Register, mask: u8) {
 | 
			
		||||
        debug_assert!(register.is_eth_register());
 | 
			
		||||
 | 
			
		||||
        self.change_bank(register);
 | 
			
		||||
 | 
			
		||||
        self.spi
 | 
			
		||||
            .write(&[Instruction::BFC.opcode() | register.addr(), mask])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bit_field_set<R>(&mut self, register: R, mask: u8)
 | 
			
		||||
    where
 | 
			
		||||
        R: Into<Register>,
 | 
			
		||||
    {
 | 
			
		||||
        self._bit_field_set(register.into(), mask)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn _bit_field_set(&mut self, register: Register, mask: u8) {
 | 
			
		||||
        debug_assert!(register.is_eth_register());
 | 
			
		||||
 | 
			
		||||
        self.change_bank(register);
 | 
			
		||||
 | 
			
		||||
        self.spi
 | 
			
		||||
            .write(&[Instruction::BFS.opcode() | register.addr(), mask])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn read_buffer_memory(&mut self, addr: Option<u16>, buf: &mut [u8]) {
 | 
			
		||||
        if let Some(addr) = addr {
 | 
			
		||||
            self.write_control_register(bank0::Register::ERDPTL, addr.low());
 | 
			
		||||
            self.write_control_register(bank0::Register::ERDPTH, addr.high());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.spi
 | 
			
		||||
            .transaction(&mut [Operation::Write(&[Instruction::RBM.opcode()]), Operation::Read(buf)])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn soft_reset(&mut self) {
 | 
			
		||||
        self.spi.write(&[Instruction::SRC.opcode()]).unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn write_buffer_memory(&mut self, addr: Option<u16>, buffer: &[u8]) {
 | 
			
		||||
        if let Some(addr) = addr {
 | 
			
		||||
            self.write_control_register(bank0::Register::EWRPTL, addr.low());
 | 
			
		||||
            self.write_control_register(bank0::Register::EWRPTH, addr.high());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.spi
 | 
			
		||||
            .transaction(&mut [Operation::Write(&[Instruction::WBM.opcode()]), Operation::Write(buffer)])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq)]
 | 
			
		||||
enum Bank {
 | 
			
		||||
    Bank0,
 | 
			
		||||
    Bank1,
 | 
			
		||||
    Bank2,
 | 
			
		||||
    Bank3,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
enum Instruction {
 | 
			
		||||
    /// Read Control Register
 | 
			
		||||
    RCR = 0b000_00000,
 | 
			
		||||
    /// Read Buffer Memory
 | 
			
		||||
    RBM = 0b001_11010,
 | 
			
		||||
    /// Write Control Register
 | 
			
		||||
    WCR = 0b010_00000,
 | 
			
		||||
    /// Write Buffer Memory
 | 
			
		||||
    WBM = 0b011_11010,
 | 
			
		||||
    /// Bit Field Set
 | 
			
		||||
    BFS = 0b100_00000,
 | 
			
		||||
    /// Bit Field Clear
 | 
			
		||||
    BFC = 0b101_00000,
 | 
			
		||||
    /// System Reset Command
 | 
			
		||||
    SRC = 0b111_11111,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Instruction {
 | 
			
		||||
    fn opcode(&self) -> u8 {
 | 
			
		||||
        *self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
enum Register {
 | 
			
		||||
    Bank0(bank0::Register),
 | 
			
		||||
    Bank1(bank1::Register),
 | 
			
		||||
    Bank2(bank2::Register),
 | 
			
		||||
    Bank3(bank3::Register),
 | 
			
		||||
    Common(common::Register),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Register {
 | 
			
		||||
    fn addr(&self) -> u8 {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Register::Bank0(r) => r.addr(),
 | 
			
		||||
            Register::Bank1(r) => r.addr(),
 | 
			
		||||
            Register::Bank2(r) => r.addr(),
 | 
			
		||||
            Register::Bank3(r) => r.addr(),
 | 
			
		||||
            Register::Common(r) => r.addr(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bank(&self) -> Option<Bank> {
 | 
			
		||||
        Some(match *self {
 | 
			
		||||
            Register::Bank0(_) => Bank::Bank0,
 | 
			
		||||
            Register::Bank1(_) => Bank::Bank1,
 | 
			
		||||
            Register::Bank2(_) => Bank::Bank2,
 | 
			
		||||
            Register::Bank3(_) => Bank::Bank3,
 | 
			
		||||
            Register::Common(_) => return None,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn is_eth_register(&self) -> bool {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Register::Bank0(r) => r.is_eth_register(),
 | 
			
		||||
            Register::Bank1(r) => r.is_eth_register(),
 | 
			
		||||
            Register::Bank2(r) => r.is_eth_register(),
 | 
			
		||||
            Register::Bank3(r) => r.is_eth_register(),
 | 
			
		||||
            Register::Common(r) => r.is_eth_register(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Packet type, used to configure receive filters
 | 
			
		||||
#[non_exhaustive]
 | 
			
		||||
#[derive(Clone, Copy, Eq, PartialEq)]
 | 
			
		||||
pub enum Packet {
 | 
			
		||||
    /// Broadcast packets
 | 
			
		||||
    Broadcast,
 | 
			
		||||
    /// Multicast packets
 | 
			
		||||
    Multicast,
 | 
			
		||||
    /// Unicast packets
 | 
			
		||||
    Unicast,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static mut TX_BUF: [u8; MTU] = [0; MTU];
 | 
			
		||||
static mut RX_BUF: [u8; MTU] = [0; MTU];
 | 
			
		||||
 | 
			
		||||
impl<S, O> embassy_net_driver::Driver for Enc28j60<S, O>
 | 
			
		||||
where
 | 
			
		||||
    S: SpiDevice,
 | 
			
		||||
    O: OutputPin,
 | 
			
		||||
{
 | 
			
		||||
    type RxToken<'a> = RxToken<'a>
 | 
			
		||||
    where
 | 
			
		||||
        Self: 'a;
 | 
			
		||||
 | 
			
		||||
    type TxToken<'a> = TxToken<'a, S, O>
 | 
			
		||||
    where
 | 
			
		||||
        Self: 'a;
 | 
			
		||||
 | 
			
		||||
    fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
 | 
			
		||||
        let rx_buf = unsafe { &mut RX_BUF };
 | 
			
		||||
        let tx_buf = unsafe { &mut TX_BUF };
 | 
			
		||||
        if let Some(pkt) = self.receive(rx_buf) {
 | 
			
		||||
            let n = pkt.len();
 | 
			
		||||
            Some((RxToken { buf: &mut pkt[..n] }, TxToken { buf: tx_buf, eth: self }))
 | 
			
		||||
        } else {
 | 
			
		||||
            cx.waker().wake_by_ref();
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn transmit(&mut self, _cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
 | 
			
		||||
        let tx_buf = unsafe { &mut TX_BUF };
 | 
			
		||||
        Some(TxToken { buf: tx_buf, eth: self })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn link_state(&mut self, cx: &mut core::task::Context) -> LinkState {
 | 
			
		||||
        cx.waker().wake_by_ref();
 | 
			
		||||
        match self.is_link_up() {
 | 
			
		||||
            true => LinkState::Up,
 | 
			
		||||
            false => LinkState::Down,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn capabilities(&self) -> Capabilities {
 | 
			
		||||
        let mut caps = Capabilities::default();
 | 
			
		||||
        caps.max_transmission_unit = MTU;
 | 
			
		||||
        caps.medium = Medium::Ethernet;
 | 
			
		||||
        caps
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn hardware_address(&self) -> HardwareAddress {
 | 
			
		||||
        HardwareAddress::Ethernet(self.mac_addr)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// embassy-net RX token.
 | 
			
		||||
pub struct RxToken<'a> {
 | 
			
		||||
    buf: &'a mut [u8],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> embassy_net_driver::RxToken for RxToken<'a> {
 | 
			
		||||
    fn consume<R, F>(self, f: F) -> R
 | 
			
		||||
    where
 | 
			
		||||
        F: FnOnce(&mut [u8]) -> R,
 | 
			
		||||
    {
 | 
			
		||||
        f(self.buf)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// embassy-net TX token.
 | 
			
		||||
pub struct TxToken<'a, S, O>
 | 
			
		||||
where
 | 
			
		||||
    S: SpiDevice,
 | 
			
		||||
    O: OutputPin,
 | 
			
		||||
{
 | 
			
		||||
    eth: &'a mut Enc28j60<S, O>,
 | 
			
		||||
    buf: &'a mut [u8],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a, S, O> embassy_net_driver::TxToken for TxToken<'a, S, O>
 | 
			
		||||
where
 | 
			
		||||
    S: SpiDevice,
 | 
			
		||||
    O: OutputPin,
 | 
			
		||||
{
 | 
			
		||||
    fn consume<R, F>(self, len: usize, f: F) -> R
 | 
			
		||||
    where
 | 
			
		||||
        F: FnOnce(&mut [u8]) -> R,
 | 
			
		||||
    {
 | 
			
		||||
        assert!(len <= self.buf.len());
 | 
			
		||||
        let r = f(&mut self.buf[..len]);
 | 
			
		||||
        self.eth.transmit(&self.buf[..len]);
 | 
			
		||||
        r
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								embassy-net-enc28j60/src/macros.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								embassy-net-enc28j60/src/macros.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,89 @@
 | 
			
		||||
macro_rules! register {
 | 
			
		||||
    ($REGISTER:ident, $reset_value:expr, $uxx:ty, {
 | 
			
		||||
        $(#[$($attr:tt)*] $bitfield:ident @ $range:expr,)+
 | 
			
		||||
    }) => {
 | 
			
		||||
        #[derive(Clone, Copy)]
 | 
			
		||||
        pub(crate) struct $REGISTER<MODE> {
 | 
			
		||||
            bits: $uxx,
 | 
			
		||||
            _mode: ::core::marker::PhantomData<MODE>,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $REGISTER<super::traits::Mask> {
 | 
			
		||||
            #[allow(dead_code)]
 | 
			
		||||
            pub(crate) fn mask() -> $REGISTER<super::traits::Mask> {
 | 
			
		||||
                $REGISTER { bits: 0, _mode: ::core::marker::PhantomData }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $(
 | 
			
		||||
                #[allow(dead_code)]
 | 
			
		||||
                pub(crate) fn $bitfield(&self) -> $uxx {
 | 
			
		||||
                    use super::traits::OffsetSize;
 | 
			
		||||
 | 
			
		||||
                    let size = $range.size();
 | 
			
		||||
                    let offset = $range.offset();
 | 
			
		||||
                    ((1 << size) - 1) << offset
 | 
			
		||||
                }
 | 
			
		||||
            )+
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl ::core::default::Default for $REGISTER<super::traits::W> {
 | 
			
		||||
            fn default() -> Self {
 | 
			
		||||
                $REGISTER { bits: $reset_value, _mode: ::core::marker::PhantomData }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[allow(non_snake_case)]
 | 
			
		||||
        #[allow(dead_code)]
 | 
			
		||||
        pub(crate) fn $REGISTER(bits: $uxx) -> $REGISTER<super::traits::R> {
 | 
			
		||||
            $REGISTER { bits, _mode: ::core::marker::PhantomData }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $REGISTER<super::traits::R> {
 | 
			
		||||
            #[allow(dead_code)]
 | 
			
		||||
            pub(crate) fn modify(self) -> $REGISTER<super::traits::W> {
 | 
			
		||||
                $REGISTER { bits: self.bits, _mode: ::core::marker::PhantomData }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $(
 | 
			
		||||
                #[$($attr)*]
 | 
			
		||||
                #[allow(dead_code)]
 | 
			
		||||
                pub(crate) fn $bitfield(&self) -> $uxx {
 | 
			
		||||
                    use super::traits::OffsetSize;
 | 
			
		||||
 | 
			
		||||
                    let offset = $range.offset();
 | 
			
		||||
                    let size = $range.size();
 | 
			
		||||
                    let mask = (1 << size) - 1;
 | 
			
		||||
 | 
			
		||||
                    (self.bits >> offset) & mask
 | 
			
		||||
                }
 | 
			
		||||
            )+
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $REGISTER<super::traits::W> {
 | 
			
		||||
            #[allow(dead_code)]
 | 
			
		||||
            pub(crate) fn bits(self) -> $uxx {
 | 
			
		||||
                self.bits
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $(
 | 
			
		||||
                #[$($attr)*]
 | 
			
		||||
                #[allow(dead_code)]
 | 
			
		||||
                pub(crate) fn $bitfield(&mut self, mut bits: $uxx) -> &mut Self {
 | 
			
		||||
                    use super::traits::OffsetSize;
 | 
			
		||||
 | 
			
		||||
                    let offset = $range.offset();
 | 
			
		||||
                    let size = $range.size();
 | 
			
		||||
                    let mask = (1 << size) - 1;
 | 
			
		||||
 | 
			
		||||
                    debug_assert!(bits <= mask);
 | 
			
		||||
                    bits &= mask;
 | 
			
		||||
 | 
			
		||||
                    self.bits &= !(mask << offset);
 | 
			
		||||
                    self.bits |= bits << offset;
 | 
			
		||||
 | 
			
		||||
                    self
 | 
			
		||||
                }
 | 
			
		||||
            )+
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								embassy-net-enc28j60/src/phy.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								embassy-net-enc28j60/src/phy.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum Register {
 | 
			
		||||
    PHCON1 = 0x00,
 | 
			
		||||
    PHSTAT1 = 0x01,
 | 
			
		||||
    PHID1 = 0x02,
 | 
			
		||||
    PHID2 = 0x03,
 | 
			
		||||
    PHCON2 = 0x10,
 | 
			
		||||
    PHSTAT2 = 0x11,
 | 
			
		||||
    PHIE = 0x12,
 | 
			
		||||
    PHIR = 0x13,
 | 
			
		||||
    PHLCON = 0x14,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Register {
 | 
			
		||||
    pub(crate) fn addr(&self) -> u8 {
 | 
			
		||||
        *self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
register!(PHCON2, 0, u16, {
 | 
			
		||||
    #[doc = "PHY Half-Duplex Loopback Disable bit"]
 | 
			
		||||
    hdldis @ 8,
 | 
			
		||||
    #[doc = "Jabber Correction Disable bit"]
 | 
			
		||||
    jabber @ 10,
 | 
			
		||||
    #[doc = "Twisted-Pair Transmitter Disable bit"]
 | 
			
		||||
    txdis @ 13,
 | 
			
		||||
    #[doc = "PHY Force Linkup bit"]
 | 
			
		||||
    frclnk @ 14,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
register!(PHSTAT2, 0, u16, {
 | 
			
		||||
    #[doc = "Link Status bit"]
 | 
			
		||||
    lstat @ 10,
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										57
									
								
								embassy-net-enc28j60/src/traits.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								embassy-net-enc28j60/src/traits.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
use core::ops::Range;
 | 
			
		||||
 | 
			
		||||
pub(crate) trait OffsetSize {
 | 
			
		||||
    fn offset(self) -> u8;
 | 
			
		||||
    fn size(self) -> u8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl OffsetSize for u8 {
 | 
			
		||||
    fn offset(self) -> u8 {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn size(self) -> u8 {
 | 
			
		||||
        1
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl OffsetSize for Range<u8> {
 | 
			
		||||
    fn offset(self) -> u8 {
 | 
			
		||||
        self.start
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn size(self) -> u8 {
 | 
			
		||||
        self.end - self.start
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) trait U16Ext {
 | 
			
		||||
    fn from_parts(low: u8, high: u8) -> Self;
 | 
			
		||||
 | 
			
		||||
    fn low(self) -> u8;
 | 
			
		||||
 | 
			
		||||
    fn high(self) -> u8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl U16Ext for u16 {
 | 
			
		||||
    fn from_parts(low: u8, high: u8) -> u16 {
 | 
			
		||||
        ((high as u16) << 8) + low as u16
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn low(self) -> u8 {
 | 
			
		||||
        (self & 0xff) as u8
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn high(self) -> u8 {
 | 
			
		||||
        (self >> 8) as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub struct Mask;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub struct R;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub struct W;
 | 
			
		||||
@ -12,6 +12,7 @@ nightly = [
 | 
			
		||||
    "embassy-nrf/nightly",
 | 
			
		||||
    "embassy-net/nightly",
 | 
			
		||||
    "embassy-net-esp-hosted",
 | 
			
		||||
    "embassy-net-enc28j60",
 | 
			
		||||
    "embassy-nrf/unstable-traits",
 | 
			
		||||
    "embassy-time/nightly",
 | 
			
		||||
    "embassy-time/unstable-traits",
 | 
			
		||||
@ -40,6 +41,7 @@ lora-phy = { version = "1", optional = true }
 | 
			
		||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
 | 
			
		||||
lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }
 | 
			
		||||
embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true }
 | 
			
		||||
embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"], optional = true }
 | 
			
		||||
 | 
			
		||||
defmt = "0.3"
 | 
			
		||||
defmt-rtt = "0.4"
 | 
			
		||||
@ -54,7 +56,9 @@ rand = { version = "0.8.4", default-features = false }
 | 
			
		||||
embedded-storage = "0.3.0"
 | 
			
		||||
usbd-hid = "0.6.0"
 | 
			
		||||
serde = { version = "1.0.136", default-features = false }
 | 
			
		||||
embedded-hal = { version = "1.0.0-alpha.11" }
 | 
			
		||||
embedded-hal-async = { version = "0.2.0-alpha.2", optional = true }
 | 
			
		||||
embedded-hal-bus = { version = "0.1.0-alpha.3" }
 | 
			
		||||
num-integer = { version = "0.1.45", default-features = false }
 | 
			
		||||
microfft = "0.5.0"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										124
									
								
								examples/nrf52840/src/bin/ethernet_enc28j60.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								examples/nrf52840/src/bin/ethernet_enc28j60.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,124 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
use defmt::*;
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
use embassy_net::tcp::TcpSocket;
 | 
			
		||||
use embassy_net::{Stack, StackResources};
 | 
			
		||||
use embassy_net_enc28j60::Enc28j60;
 | 
			
		||||
use embassy_nrf::gpio::{Level, Output, OutputDrive};
 | 
			
		||||
use embassy_nrf::rng::Rng;
 | 
			
		||||
use embassy_nrf::spim::Spim;
 | 
			
		||||
use embassy_nrf::{bind_interrupts, peripherals, spim};
 | 
			
		||||
use embassy_time::Delay;
 | 
			
		||||
use embedded_hal_bus::spi::ExclusiveDevice;
 | 
			
		||||
use embedded_io_async::Write;
 | 
			
		||||
use static_cell::make_static;
 | 
			
		||||
use {defmt_rtt as _, panic_probe as _};
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
 | 
			
		||||
    RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::task]
 | 
			
		||||
async fn net_task(
 | 
			
		||||
    stack: &'static Stack<
 | 
			
		||||
        Enc28j60<
 | 
			
		||||
            ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static, peripherals::P0_15>, Delay>,
 | 
			
		||||
            Output<'static, peripherals::P0_13>,
 | 
			
		||||
        >,
 | 
			
		||||
    >,
 | 
			
		||||
) -> ! {
 | 
			
		||||
    stack.run().await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
async fn main(spawner: Spawner) {
 | 
			
		||||
    let p = embassy_nrf::init(Default::default());
 | 
			
		||||
    info!("running!");
 | 
			
		||||
 | 
			
		||||
    let eth_sck = p.P0_20;
 | 
			
		||||
    let eth_mosi = p.P0_22;
 | 
			
		||||
    let eth_miso = p.P0_24;
 | 
			
		||||
    let eth_cs = p.P0_15;
 | 
			
		||||
    let eth_rst = p.P0_13;
 | 
			
		||||
    let _eth_irq = p.P0_12;
 | 
			
		||||
 | 
			
		||||
    let mut config = spim::Config::default();
 | 
			
		||||
    config.frequency = spim::Frequency::M16;
 | 
			
		||||
    let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config);
 | 
			
		||||
    let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard);
 | 
			
		||||
    let spi = ExclusiveDevice::new(spi, cs, Delay);
 | 
			
		||||
 | 
			
		||||
    let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard);
 | 
			
		||||
    let mac_addr = [2, 3, 4, 5, 6, 7];
 | 
			
		||||
    let device = Enc28j60::new(spi, Some(rst), mac_addr);
 | 
			
		||||
 | 
			
		||||
    let config = embassy_net::Config::dhcpv4(Default::default());
 | 
			
		||||
    // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
 | 
			
		||||
    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
 | 
			
		||||
    //    dns_servers: Vec::new(),
 | 
			
		||||
    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
 | 
			
		||||
    // });
 | 
			
		||||
 | 
			
		||||
    // Generate random seed
 | 
			
		||||
    let mut rng = Rng::new(p.RNG, Irqs);
 | 
			
		||||
    let mut seed = [0; 8];
 | 
			
		||||
    rng.blocking_fill_bytes(&mut seed);
 | 
			
		||||
    let seed = u64::from_le_bytes(seed);
 | 
			
		||||
 | 
			
		||||
    // Init network stack
 | 
			
		||||
    let stack = &*make_static!(Stack::new(
 | 
			
		||||
        device,
 | 
			
		||||
        config,
 | 
			
		||||
        make_static!(StackResources::<2>::new()),
 | 
			
		||||
        seed
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    unwrap!(spawner.spawn(net_task(stack)));
 | 
			
		||||
 | 
			
		||||
    // And now we can use it!
 | 
			
		||||
 | 
			
		||||
    let mut rx_buffer = [0; 4096];
 | 
			
		||||
    let mut tx_buffer = [0; 4096];
 | 
			
		||||
    let mut buf = [0; 4096];
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
 | 
			
		||||
        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));
 | 
			
		||||
 | 
			
		||||
        info!("Listening on TCP:1234...");
 | 
			
		||||
        if let Err(e) = socket.accept(1234).await {
 | 
			
		||||
            warn!("accept error: {:?}", e);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        info!("Received connection from {:?}", socket.remote_endpoint());
 | 
			
		||||
 | 
			
		||||
        loop {
 | 
			
		||||
            let n = match socket.read(&mut buf).await {
 | 
			
		||||
                Ok(0) => {
 | 
			
		||||
                    warn!("read EOF");
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                Ok(n) => n,
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    warn!("read error: {:?}", e);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            info!("rxd {:02x}", &buf[..n]);
 | 
			
		||||
 | 
			
		||||
            match socket.write_all(&buf[..n]).await {
 | 
			
		||||
                Ok(()) => {}
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    warn!("write error: {:?}", e);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user