diff --git a/src/lib.rs b/src/lib.rs index 132499a..efad811 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,93 +12,107 @@ use stm32f4xx_hal::{ }; pub mod rx; +pub mod tx; #[cfg(feature="smoltcp")] pub mod smoltcp_phy; pub trait EthController { - fn init_dev(&mut self) -> Result<(), EthControllerError>; - fn init_rxbuf(&mut self) -> Result<(), EthControllerError>; - fn receive_next(&mut self) -> Result; - fn set_promiscuous(&mut self) -> Result<(), EthControllerError>; - fn read_from_mac(&mut self, mac: &mut [u8]) -> Result<(), EthControllerError>; + 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 set_promiscuous(&mut self) -> Result<(), EthControllerError>; + fn read_from_mac(&mut self, mac: &mut [u8]) -> Result<(), EthControllerError>; } /// TODO: Improve these error types pub enum EthControllerError { - SpiPortError, - GeneralError + SpiPortError, + GeneralError } impl From for EthControllerError { - fn from(e: spi::SpiPortError) -> EthControllerError { - EthControllerError::SpiPortError - } + fn from(e: spi::SpiPortError) -> EthControllerError { + EthControllerError::SpiPortError + } } /// Ethernet controller using SPI interface pub struct SpiEth, NSS: OutputPin> { - spi_port: spi::SpiPort, - rx_buf: rx::RxBuffer + spi_port: spi::SpiPort, + rx_buf: rx::RxBuffer, + tx_buf: tx::TxBuffer } impl , NSS: OutputPin> SpiEth { pub fn new(spi: SPI, mut nss: NSS) -> Self { - SpiEth { - spi_port: spi::SpiPort::new(spi, nss), - rx_buf: rx::RxBuffer::new(), - // TODO: tx_buf - } + 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 { fn init_dev(&mut self) -> Result<(), EthControllerError> { - // Write 0x1234 to EUDAST - self.spi_port.write_reg_16b(spi::EUDAST, 0x1234)?; - // Verify that EUDAST is 0x1234 - let mut eudast = self.spi_port.read_reg_16b(spi::EUDAST)?; - if eudast != 0x1234 { - return Err(EthControllerError::GeneralError) - } - // Poll CLKRDY (ESTAT<12>) to check if it is set - loop { - let estat = self.spi_port.read_reg_16b(spi::ESTAT)?; - if estat & 0x1000 == 0x1000 { break } - } - // Set ETHRST (ECON2<4>) to 1 - let econ2 = self.spi_port.read_reg_8b(spi::ECON2)?; - self.spi_port.write_reg_8b(spi::ECON2, 0x10 | (econ2 & 0b11101111))?; - // Verify that EUDAST is 0x0000 - eudast = self.spi_port.read_reg_16b(spi::EUDAST)?; - if eudast != 0x0000 { - return Err(EthControllerError::GeneralError) - } - Ok(()) + // Write 0x1234 to EUDAST + self.spi_port.write_reg_16b(spi::EUDAST, 0x1234)?; + // Verify that EUDAST is 0x1234 + let mut eudast = self.spi_port.read_reg_16b(spi::EUDAST)?; + if eudast != 0x1234 { + return Err(EthControllerError::GeneralError) + } + // Poll CLKRDY (ESTAT<12>) to check if it is set + loop { + let estat = self.spi_port.read_reg_16b(spi::ESTAT)?; + if estat & 0x1000 == 0x1000 { break } + } + // Set ETHRST (ECON2<4>) to 1 + let econ2 = self.spi_port.read_reg_8b(spi::ECON2)?; + self.spi_port.write_reg_8b(spi::ECON2, 0x10 | (econ2 & 0b11101111))?; + // Verify that EUDAST is 0x0000 + eudast = self.spi_port.read_reg_16b(spi::EUDAST)?; + if eudast != 0x0000 { + return Err(EthControllerError::GeneralError) + } + Ok(()) } fn init_rxbuf(&mut self) -> Result<(), EthControllerError> { - // Set ERXST pointer - self.spi_port.write_reg_16b(spi::ERXST, self.rx_buf.get_wrap_addr()); + // Set ERXST pointer + self.spi_port.write_reg_16b(spi::ERXST, self.rx_buf.get_wrap_addr())?; // Set ERXTAIL pointer - self.spi_port.write_reg_16b(spi::ERXTAIL, self.rx_buf.get_tail_addr()); + self.spi_port.write_reg_16b(spi::ERXTAIL, self.rx_buf.get_tail_addr())?; // Set MAMXFL to maximum number of bytes in each accepted packet - self.spi_port.write_reg_16b(spi::MAMXFL, rx::RAW_FRAME_LENGTH_MAX as u16); + 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)); 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 { // 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 } + let eir = self.spi_port.read_reg_16b(spi::EIR)?; + if eir & 0x40 == 0x40 { break } } // Set ERXRDPT pointer to next_addr self.spi_port.write_reg_16b(spi::ERXRDPT, self.rx_buf.get_next_addr())?; @@ -125,31 +139,60 @@ impl , } else { self.spi_port.write_reg_16b(spi::ERXTAIL, rx::RX_MAX_ADDRESS - 1)?; } - // Set PKTDEC to decrement PKTCNT + // Set PKTDEC (ECON1<88>) to decrement PKTCNT let econ1_hi = self.spi_port.read_reg_8b(spi::ECON1 + 1)?; self.spi_port.write_reg_8b(spi::ECON1 + 1, 0x01 | (econ1_hi & 0xfe))?; // Return the RxPacket Ok(rx_packet) } + /// 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> { + // 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..]); + 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())?; + // Set ETXLEN to packet length + self.spi_port.write_reg_16b(spi::ETXLEN, packet.get_frame_length() as u16)?; + // Set TXRTS (ECON1<1>) to start transmission + let mut econ1_lo = self.spi_port.read_reg_8b(spi::ECON1)?; + self.spi_port.write_reg_8b(spi::ECON1, 0x02 | (econ1_lo & 0xfd))?; + // Poll TXRTS (ECON1<1>) to check if it is reset + loop { + econ1_lo = self.spi_port.read_reg_8b(spi::ECON1)?; + if econ1_lo & 0x02 == 0x02 { break } + } + // TODO: Read ETXSTAT + // 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); + Ok(()) + } + /// Set controller to Promiscuous Mode fn set_promiscuous(&mut self) -> Result<(), EthControllerError> { - // From ENC424J600 Data Sheet Section 10.12: - // "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)); - Ok(()) + // From ENC424J600 Data Sheet Section 10.12: + // "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)); + Ok(()) } /// Read MAC to [u8; 6] - fn read_from_mac(&mut self, mac: &mut [u8]) -> Result<(), EthControllerError> { - mac[0] = self.spi_port.read_reg_8b(spi::MAADR1)?; - mac[1] = self.spi_port.read_reg_8b(spi::MAADR1 + 1)?; - mac[2] = self.spi_port.read_reg_8b(spi::MAADR2)?; - mac[3] = self.spi_port.read_reg_8b(spi::MAADR2 + 1)?; - mac[4] = self.spi_port.read_reg_8b(spi::MAADR3)?; - mac[5] = self.spi_port.read_reg_8b(spi::MAADR3 + 1)?; - Ok(()) - } + fn read_from_mac(&mut self, mac: &mut [u8]) -> Result<(), EthControllerError> { + mac[0] = self.spi_port.read_reg_8b(spi::MAADR1)?; + mac[1] = self.spi_port.read_reg_8b(spi::MAADR1 + 1)?; + mac[2] = self.spi_port.read_reg_8b(spi::MAADR2)?; + mac[3] = self.spi_port.read_reg_8b(spi::MAADR2 + 1)?; + mac[4] = self.spi_port.read_reg_8b(spi::MAADR3)?; + mac[5] = self.spi_port.read_reg_8b(spi::MAADR3 + 1)?; + Ok(()) + } } diff --git a/src/rx.rs b/src/rx.rs index f5a4a72..774446f 100644 --- a/src/rx.rs +++ b/src/rx.rs @@ -86,6 +86,7 @@ impl RxPacket { } } + /// TODO: Mostly for debugging only? pub fn get_frame_byte(&self, i: usize) -> u8 { self.frame[i] } diff --git a/src/spi.rs b/src/spi.rs index 8b6ebfd..f06e7d3 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -23,7 +23,8 @@ pub const SPI_CLOCK: MegaHertz = MegaHertz(14); /// SPI Opcodes const RCRU: u8 = 0b0010_0000; const WCRU: u8 = 0b0010_0010; -const ERXDATA: u8 = 0b0010_1100; // Treated as 8-bit opcode followed by data +const RERXDATA: u8 = 0b0010_1100; // 8-bit opcode followed by data +const WEGPDATA: u8 = 0b0010_1010; // 8-bit opcode followed by data /// SPI Register Mapping /// Note: PSP interface use different address mapping @@ -44,6 +45,11 @@ pub const ERXTAIL: u8 = 0x06; // 16-bit data pub const EIR: u8 = 0x1c; // 16-bit data pub const ECON1: u8 = 0x1e; // 16-bit data pub const MAMXFL: u8 = 0x4a; // 16-bit data +// TX Registers +pub const EGPWRPT: u8 = 0x88; // 16-bit data +pub const ETXST: u8 = 0x00; // 16-bit data +pub const ETXSTAT: u8 = 0x12; // 16-bit data +pub const ETXLEN: u8 = 0x02; // 16-bit data /// Struct for SPI I/O interface on ENC424J600 /// Note: stm32f4xx_hal::spi's pins include: SCK, MISO, MOSI @@ -82,13 +88,21 @@ impl , Ok(((r_data_hi as u16) << 8) | r_data_lo as u16) } - // Currently requires manual slicing (buf[1:]) for the data read back + // Currently requires manual slicing (buf[1..]) for the data read back pub fn read_rxdat<'a>(&mut self, buf: &'a mut [u8], data_length: u32) - -> Result { - let r_valid = self.r_n(buf, ERXDATA, data_length)?; + -> Result<(), SpiPortError> { + let r_valid = self.r_n(buf, RERXDATA, data_length)?; Ok(r_valid) } + // Currenly requires actual data to be stored in buf[1..] instead of buf[0..] + // TODO: Maybe better naming? + pub fn write_txdat<'a>(&mut self, buf: &'a mut [u8], data_length: u32) + -> Result<(), SpiPortError> { + let w_valid = self.w_n(buf, WEGPDATA, data_length)?; + Ok(w_valid) + } + pub fn write_reg_8b(&mut self, addr: u8, data: u8) -> Result<(), SpiPortError> { // TODO: addr should be separated from w_data // Using WCRU instruction to write using unbanked (full) address @@ -136,17 +150,40 @@ impl , // Note: buf must be at least (data_length + 1)-byte long // TODO: Check and raise error for array size < (data_length + 1) fn r_n<'a>(&mut self, buf: &'a mut [u8], opcode: u8, data_length: u32) - -> Result { + -> Result<(), SpiPortError> { // Enable chip select self.nss.set_low(); // Start writing to SLAVE buf[0] = opcode; match self.spi.transfer(buf) { - // TODO: Now returns a boolean, maybe use Option later on? Ok(_) => { // Disable chip select self.nss.set_high(); - Ok(1) + Ok(()) + }, + // TODO: Maybe too naive? + Err(e) => { + // Disable chip select + self.nss.set_high(); + Err(SpiPortError::TransferError) + } + } + } + + // Note: buf[0] is currently reserved for opcode to overwrite + // TODO: Actual data should start from buf[0], not buf[1] + fn w_n<'a>(&mut self, buf: &'a mut [u8], opcode: u8, data_length: u32) + -> Result<(), SpiPortError> { + // Enable chip select + self.nss.set_low(); + // Start writing to SLAVE + buf[0] = opcode; + // TODO: Maybe need to copy data to buf later on + match self.spi.transfer(buf) { + Ok(_) => { + // Disable chip select + self.nss.set_high(); + Ok(()) }, // TODO: Maybe too naive? Err(e) => { diff --git a/src/tx.rs b/src/tx.rs new file mode 100644 index 0000000..994dc2c --- /dev/null +++ b/src/tx.rs @@ -0,0 +1,79 @@ +/// SRAM Addresses +pub const GPBUFST_DEFAULT: u16 = 0x0000; // Start of General-Purpose SRAM Buffer +pub const GPBUFEN_DEFAULT: u16 = 0x5340; // End of General-Purpose SRAM Buffer == ERXST default + +/// Max raw frame array size +pub const RAW_FRAME_LENGTH_MAX: usize = 0x1000; + +/// Struct for TX Buffer +/// TODO: Should be a singleton +pub struct TxBuffer { + wrap_addr: u16, + // The following two fields are controlled by firmware + next_addr: u16, + tail_addr: u16 +} + +impl TxBuffer { + pub fn new() -> Self { + TxBuffer { + wrap_addr: GPBUFST_DEFAULT, + next_addr: GPBUFST_DEFAULT + 1, + tail_addr: GPBUFST_DEFAULT + } + } + + pub fn set_wrap_addr(&mut self, addr: u16) { + self.wrap_addr = addr; + } + pub fn get_wrap_addr(& self) -> u16{ + self.wrap_addr + } + + pub fn set_next_addr(&mut self, addr: u16) { + self.next_addr = addr; + } + pub fn get_next_addr(& self) -> u16{ + self.next_addr + } + + // TODO: Need more functions for smoltcp::phy compatibility (maybe?) +} + +/// Struct for TX Packet +/// TODO: Generalise MAC addresses +pub struct TxPacket { + frame: [u8; RAW_FRAME_LENGTH_MAX], + frame_length: usize +} + +impl TxPacket { + pub fn new() -> Self { + TxPacket { + frame: [0; RAW_FRAME_LENGTH_MAX], + frame_length: 0 + } + } + + /// Currently, frame data is copied from an external buffer + pub fn update_frame(&mut self, raw_frame: &[u8], raw_frame_length: usize) { + self.frame_length = raw_frame_length; + for i in 0..self.frame_length { + self.frame[i] = raw_frame[i]; + } + } + pub fn copy_from_frame(&self, frame: &mut [u8]) { + for i in 0..self.frame_length { + frame[i] = self.frame[i]; + } + } + + pub fn get_frame_length(&self) -> usize { + self.frame_length + } + + /// TODO: Mostly for debugging only? + pub fn get_frame_byte(&self, i: usize) -> u8 { + self.frame[i] + } +}