From dd062723a399488a491a305c42653294c12d7b4b Mon Sep 17 00:00:00 2001 From: Harry Ho Date: Wed, 24 Jun 2020 14:17:07 +0800 Subject: [PATCH] Add smoltcp Phy impls --- Cargo.lock | 12 ++--- Cargo.toml | 10 +++- src/lib.rs | 51 ++++++++++---------- src/rx.rs | 13 ++++- src/smoltcp_phy.rs | 116 +++++++++++++++++++++++++++++++++++++++++++++ src/tx.rs | 20 ++++++-- 6 files changed, 184 insertions(+), 38 deletions(-) create mode 100644 src/smoltcp_phy.rs diff --git a/Cargo.lock b/Cargo.lock index a34ec7f..0b9d77d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "embedded-hal" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" +checksum = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb" dependencies = [ "nb", "void", @@ -281,9 +281,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.31" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" +checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" dependencies = [ "proc-macro2", "quote", @@ -298,9 +298,9 @@ checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "vcell" diff --git a/Cargo.toml b/Cargo.toml index a49d9c5..c3cb1c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,14 @@ volatile-register = "0.2" aligned = "0.3" stm32f4xx-hal = { version = "0.8" , optional = true } smoltcp = { version = "0.6.0", default-features = false, features = ["proto-ipv4", "proto-ipv6", "socket-icmp", "socket-udp", "socket-tcp", "log", "verbose", "ethernet"], optional = true } -log = { version = "0.4" } +log = "0.4" [features] smoltcp-phy = ["smoltcp"] +smoltcp-phy-all = [ + "smoltcp/socket-raw", "smoltcp/socket-udp", "smoltcp/socket-tcp", + "smoltcp/proto-ipv4", "smoltcp/proto-ipv6" +] stm32f407 = ["stm32f4xx-hal/stm32f407"] default = [] @@ -30,6 +34,10 @@ panic-itm = "0.4" name = "tx_stm32f407" required-features = ["stm32f407"] +[[example]] +name = "tcp_stm32f407" +required-features = ["stm32f407", "smoltcp-phy-all", "smoltcp/log"] + [profile.release] codegen-units = 1 incremental = false diff --git a/src/lib.rs b/src/lib.rs index efad811..f2e3fef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,5 @@ #![no_std] -use core::fmt; - /// STM32F4xx-HAL specific implementations pub mod spi; use stm32f4xx_hal::{ @@ -17,14 +15,12 @@ pub mod tx; #[cfg(feature="smoltcp")] pub mod smoltcp_phy; -pub trait EthController { +pub trait EthController<'c> { fn init_dev(&mut self) -> Result<(), EthControllerError>; fn init_rxbuf(&mut self) -> Result<(), EthControllerError>; - // TODO: fn init_txbuf(&mut self) -> Result<(), EthControllerError>; - fn receive_next(&mut self) -> Result; - // TODO: send_packet() is not using TxBuffer, but it should later on - fn send_raw_packet(&mut self, packet: tx::TxPacket) -> Result<(), EthControllerError>; + fn receive_next(&mut self, is_poll: bool) -> Result; + fn send_raw_packet(&mut self, packet: &tx::TxPacket) -> Result<(), EthControllerError>; fn set_promiscuous(&mut self) -> Result<(), EthControllerError>; fn read_from_mac(&mut self, mac: &mut [u8]) -> Result<(), EthControllerError>; } @@ -32,11 +28,13 @@ pub trait EthController { /// TODO: Improve these error types pub enum EthControllerError { SpiPortError, - GeneralError + GeneralError, + // TODO: Better name? + NoRxPacketError } impl From for EthControllerError { - fn from(e: spi::SpiPortError) -> EthControllerError { + fn from(_: spi::SpiPortError) -> EthControllerError { EthControllerError::SpiPortError } } @@ -51,18 +49,17 @@ pub struct SpiEth, impl , NSS: OutputPin> SpiEth { - pub fn new(spi: SPI, mut nss: NSS) -> Self { + pub fn new(spi: SPI, nss: NSS) -> Self { SpiEth { spi_port: spi::SpiPort::new(spi, nss), rx_buf: rx::RxBuffer::new(), - // TODO: tx_buf tx_buf: tx::TxBuffer::new() } } } -impl , - NSS: OutputPin> EthController for SpiEth { +impl <'c, SPI: Transfer, + NSS: OutputPin> EthController<'c> for SpiEth { fn init_dev(&mut self) -> Result<(), EthControllerError> { // Write 0x1234 to EUDAST self.spi_port.write_reg_16b(spi::EUDAST, 0x1234)?; @@ -96,23 +93,27 @@ impl , self.spi_port.write_reg_16b(spi::MAMXFL, rx::RAW_FRAME_LENGTH_MAX as u16)?; // Enable RXEN (ECON1<0>) let econ1 = self.spi_port.read_reg_16b(spi::ECON1)?; - self.spi_port.write_reg_16b(spi::ECON1, 0x1 | (econ1 & 0xfffe)); + self.spi_port.write_reg_16b(spi::ECON1, 0x1 | (econ1 & 0xfffe))?; Ok(()) } - /// TODO: fn init_txbuf(&mut self) -> Result<(), EthControllerError> { // Set EGPWRPT pointer self.spi_port.write_reg_16b(spi::EGPWRPT, 0x0000)?; Ok(()) } - /// Receive the next packet - fn receive_next(&mut self) -> Result { + /// Receive the next packet and copy it to rx_packet_buf + /// Set is_poll to true for returning until PKTIF is set; + /// Set is_poll to false for returning Err when PKTIF is not set + fn receive_next(&mut self, is_poll: bool) -> Result { // Poll PKTIF (EIR<4>) to check if it is set loop { let eir = self.spi_port.read_reg_16b(spi::EIR)?; if eir & 0x40 == 0x40 { break } + if !is_poll { + return Err(EthControllerError::NoRxPacketError) + } } // Set ERXRDPT pointer to next_addr self.spi_port.write_reg_16b(spi::ERXRDPT, self.rx_buf.get_next_addr())?; @@ -132,7 +133,7 @@ impl , // Read frame bytes let mut frame_buf = [0; rx::RAW_FRAME_LENGTH_MAX]; self.spi_port.read_rxdat(&mut frame_buf, rx_packet.get_frame_length() as u32)?; - rx_packet.write_to_frame(&frame_buf[1..]); + rx_packet.copy_frame_from(&frame_buf[1..]); // Set ERXTAIL pointer to (next_addr - 2) if self.rx_buf.get_next_addr() > rx::ERXST_DEFAULT { self.spi_port.write_reg_16b(spi::ERXTAIL, self.rx_buf.get_next_addr() - 2)?; @@ -147,14 +148,13 @@ impl , } /// Send an established packet - /// TODO: Should be eliminated when TxBuffer is used instead later on - fn send_raw_packet(&mut self, packet: tx::TxPacket) -> Result<(), EthControllerError> { + fn send_raw_packet(&mut self, packet: &tx::TxPacket) -> Result<(), EthControllerError> { // Set EGPWRPT pointer to next_addr self.spi_port.write_reg_16b(spi::EGPWRPT, self.tx_buf.get_next_addr())?; // Copy packet data to SRAM Buffer // 1-byte Opcode is included let mut txdat_buf: [u8; tx::RAW_FRAME_LENGTH_MAX + 1] = [0; tx::RAW_FRAME_LENGTH_MAX + 1]; - packet.copy_from_frame(&mut txdat_buf[1..]); + packet.write_frame_to(&mut txdat_buf[1..]); self.spi_port.write_txdat(&mut txdat_buf, packet.get_frame_length() as u32)?; // Set ETXST to packet start address self.spi_port.write_reg_16b(spi::ETXST, self.tx_buf.get_next_addr())?; @@ -168,7 +168,8 @@ impl , econ1_lo = self.spi_port.read_reg_8b(spi::ECON1)?; if econ1_lo & 0x02 == 0x02 { break } } - // TODO: Read ETXSTAT + // TODO: Read ETXSTAT to understand Ethernet transmission status + // (See: Register 9-2, ENC424J600 Data Sheet) // Update TX buffer start address self.tx_buf.set_next_addr((self.tx_buf.get_next_addr() + packet.get_frame_length() as u16) % tx::GPBUFEN_DEFAULT); @@ -177,11 +178,11 @@ impl , /// Set controller to Promiscuous Mode fn set_promiscuous(&mut self) -> Result<(), EthControllerError> { - // From ENC424J600 Data Sheet Section 10.12: + // From Section 10.12, ENC424J600 Data Sheet: // "To accept all incoming frames regardless of content (Promiscuous mode), // set the CRCEN, RUNTEN, UCEN, NOTMEEN and MCEN bits." - let mut erxfcon_lo = self.spi_port.read_reg_8b(spi::ERXFCON)?; - self.spi_port.write_reg_8b(spi::ERXFCON, 0b0101_1110 | (erxfcon_lo & 0b1010_0001)); + let erxfcon_lo = self.spi_port.read_reg_8b(spi::ERXFCON)?; + self.spi_port.write_reg_8b(spi::ERXFCON, 0b0101_1110 | (erxfcon_lo & 0b1010_0001))?; Ok(()) } diff --git a/src/rx.rs b/src/rx.rs index 774446f..15b1c85 100644 --- a/src/rx.rs +++ b/src/rx.rs @@ -8,7 +8,7 @@ pub const RAW_FRAME_LENGTH_MAX: usize = 0x1000; /// Receive Status Vector Length pub const RSV_LENGTH: usize = 6; -/// Struct for RX Buffer +/// Struct for RX Buffer on the hardware /// TODO: Should be a singleton pub struct RxBuffer { wrap_addr: u16, @@ -80,11 +80,20 @@ impl RxPacket { self.frame_length } - pub fn write_to_frame(&mut self, raw_frame: &[u8]) { + pub fn copy_frame_from(&mut self, raw_frame: &[u8]) { for i in 0..self.frame_length { self.frame[i] = raw_frame[i]; } } + pub fn write_frame_to(&self, frame: &mut [u8]) { + for i in 0..self.frame_length { + frame[i] = self.frame[i]; + } + } + + pub fn get_mut_frame(&mut self) -> &mut [u8] { + &mut self.frame + } /// TODO: Mostly for debugging only? pub fn get_frame_byte(&self, i: usize) -> u8 { diff --git a/src/smoltcp_phy.rs b/src/smoltcp_phy.rs new file mode 100644 index 0000000..d80c42c --- /dev/null +++ b/src/smoltcp_phy.rs @@ -0,0 +1,116 @@ +use crate::{ + EthController, rx, tx +}; +use core::intrinsics::transmute; +use smoltcp::{ + phy::{Device, DeviceCapabilities, RxToken, TxToken}, + time::Instant, + Error +}; + +pub struct SmoltcpDevice<'c> { + eth_controller: &'c mut dyn EthController<'c>, + rx_packet_buf: [u8; rx::RAW_FRAME_LENGTH_MAX], + tx_packet_buf: [u8; tx::RAW_FRAME_LENGTH_MAX] +} + +impl<'c> SmoltcpDevice<'c> { + pub fn new(eth_controller: &'c mut dyn EthController<'c>) -> Self { + SmoltcpDevice { + eth_controller, + rx_packet_buf: [0; rx::RAW_FRAME_LENGTH_MAX], + tx_packet_buf: [0; tx::RAW_FRAME_LENGTH_MAX] + } + } +} + +impl<'a, 'c> Device<'a> for SmoltcpDevice<'c> { + type RxToken = EthRxToken<'a>; + type TxToken = EthTxToken<'a>; + + fn capabilities(&self) -> DeviceCapabilities { + DeviceCapabilities::default() + } + + fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + // Extend self lifetime from 'c to 'a for tokens' access to EthController + let self_trans = unsafe { + transmute::<&mut SmoltcpDevice<'c>, &mut SmoltcpDevice<'a>>(&mut *self) + }; + // Make self_a point to *self that has a lifetime of 'a (extended) + let self_a = self_trans as *mut SmoltcpDevice<'a>; + match self_trans.eth_controller.receive_next(false) { + Ok(rx_packet) => { + // Write received packet to RX packet buffer + rx_packet.write_frame_to(&mut self.rx_packet_buf); + // Construct a RxToken + let rx_token = EthRxToken { + buf: &mut self.rx_packet_buf, + len: rx_packet.get_frame_length() + }; + // Construct a blank TxToken + let tx_token = EthTxToken { + buf: &mut self.tx_packet_buf, + dev: self_a + }; + Some((rx_token, tx_token)) + }, + Err(_) => None + } + } + + fn transmit(&'a mut self) -> Option { + // Extend self lifetime from 'c to 'a for TxToken's access to EthController + let self_trans = unsafe { + transmute::<&mut SmoltcpDevice<'c>, &mut SmoltcpDevice<'a>>(&mut *self) + }; + // Make self_a point to *self that has a lifetime of 'a (extended) + let self_a = self_trans as *mut SmoltcpDevice<'a>; + // Construct a blank TxToken + let tx_token = EthTxToken { + buf: &mut self.tx_packet_buf, + dev: self_a + }; + Some(tx_token) + } +} + +pub struct EthRxToken<'a> { + buf: &'a mut [u8], + len: usize +} + +impl<'a> RxToken for EthRxToken<'a> { + fn consume(self, _timestamp: Instant, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> Result, + { + f(&mut self.buf[..self.len]) + } +} + +pub struct EthTxToken<'a> { + buf: &'a mut [u8], + dev: *mut SmoltcpDevice<'a> +} + +impl<'a> TxToken for EthTxToken<'a> { + fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> Result, + { + let result = f(&mut self.buf[..len]); + // Construct a TxPacket + let mut tx_packet = tx::TxPacket::new(); + // Update frame length and write frame bytes + tx_packet.update_frame(&mut self.buf[..len], len); + // Send the packet as raw + let eth_controller = unsafe { + &mut (*self.dev).eth_controller + }; + match eth_controller.send_raw_packet(&tx_packet) { + Ok(_) => { result }, + Err(_) => Err(Error::Exhausted) + } + } +} diff --git a/src/tx.rs b/src/tx.rs index 994dc2c..5fdb5e0 100644 --- a/src/tx.rs +++ b/src/tx.rs @@ -5,7 +5,7 @@ pub const GPBUFEN_DEFAULT: u16 = 0x5340; // End of General-Purpose SRAM Buffe /// Max raw frame array size pub const RAW_FRAME_LENGTH_MAX: usize = 0x1000; -/// Struct for TX Buffer +/// Struct for TX Buffer on the hardware /// TODO: Should be a singleton pub struct TxBuffer { wrap_addr: u16, @@ -36,8 +36,13 @@ impl TxBuffer { pub fn get_next_addr(& self) -> u16{ self.next_addr } - - // TODO: Need more functions for smoltcp::phy compatibility (maybe?) + + pub fn set_tail_addr(&mut self, addr: u16) { + self.tail_addr = addr; + } + pub fn get_tail_addr(& self) -> u16{ + self.tail_addr + } } /// Struct for TX Packet @@ -62,7 +67,7 @@ impl TxPacket { self.frame[i] = raw_frame[i]; } } - pub fn copy_from_frame(&self, frame: &mut [u8]) { + pub fn write_frame_to(&self, frame: &mut [u8]) { for i in 0..self.frame_length { frame[i] = self.frame[i]; } @@ -72,6 +77,13 @@ impl TxPacket { self.frame_length } + pub fn get_frame(&self) -> &[u8] { + &self.frame + } + pub fn get_mut_frame(&mut self) -> &mut [u8] { + &mut self.frame + } + /// TODO: Mostly for debugging only? pub fn get_frame_byte(&self, i: usize) -> u8 { self.frame[i]