From 2735e6a6ee01119ef491c089d1491245226971b6 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Wed, 12 Jul 2023 17:36:05 +0300 Subject: [PATCH 1/5] chore: socket docs Signed-off-by: Lachezar Lechev --- src/socket.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/src/socket.rs b/src/socket.rs index 2a9d794..d275f64 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -12,6 +12,7 @@ pub struct Socket { } impl Socket { + /// 8 sockets available on the w5500 pub fn new(index: u8) -> Self { /* * Socket 0 is at address 0x01 @@ -75,6 +76,7 @@ impl Socket { ) -> Result { let mut data = [0u8]; bus.read_frame(self.register(), socketn::INTERRUPT, &mut data)?; + Ok(data[0] & code as u8 != 0) } @@ -179,9 +181,11 @@ impl Socket { Ok(()) } + /// Get the received bytes size of the socket's RX buffer. + /// + /// Section 4.2 of datasheet, Sn_TX_FSR address docs indicate that read must be repeated until two sequential reads are stable pub fn get_receive_size(&self, bus: &mut SpiBus) -> Result { loop { - // Section 4.2 of datasheet, Sn_TX_FSR address docs indicate that read must be repeated until two sequential reads are stable let mut sample_0 = [0u8; 2]; bus.read_frame(self.register(), socketn::RECEIVED_SIZE, &mut sample_0)?; let mut sample_1 = [0u8; 2]; @@ -192,9 +196,94 @@ impl Socket { } } + /// Get the free TX buffer size still available for this socket. + /// + /// It's cleared once we `SEND` the buffer over the socket. pub fn get_tx_free_size(&self, bus: &mut SpiBus) -> Result { let mut data = [0; 2]; bus.read_frame(self.register(), socketn::TX_FREE_SIZE, &mut data)?; Ok(u16::from_be_bytes(data)) } } + +#[cfg(test)] +mod test { + use crate::register::*; + + use super::*; + + #[test] + fn test_socket_registers() { + // Socket 0 + { + let socket_0 = Socket::new(0); + + assert_eq!(socket_0.register, SOCKET0); + assert_eq!(socket_0.tx_buffer, SOCKET0_BUFFER_TX); + assert_eq!(socket_0.rx_buffer, SOCKET0_BUFFER_RX); + } + + // Socket 1 + { + let socket_1 = Socket::new(1); + + assert_eq!(socket_1.register, SOCKET1); + assert_eq!(socket_1.tx_buffer, SOCKET1_BUFFER_TX); + assert_eq!(socket_1.rx_buffer, SOCKET1_BUFFER_RX); + } + + // Socket 2 + { + let socket_2 = Socket::new(2); + + assert_eq!(socket_2.register, SOCKET2); + assert_eq!(socket_2.tx_buffer, SOCKET2_BUFFER_TX); + assert_eq!(socket_2.rx_buffer, SOCKET2_BUFFER_RX); + } + + // Socket 3 + { + let socket_3 = Socket::new(3); + + assert_eq!(socket_3.register, SOCKET3); + assert_eq!(socket_3.tx_buffer, SOCKET3_BUFFER_TX); + assert_eq!(socket_3.rx_buffer, SOCKET3_BUFFER_RX); + } + + // Socket 4 + { + let socket_4 = Socket::new(4); + + assert_eq!(socket_4.register, SOCKET4); + assert_eq!(socket_4.tx_buffer, SOCKET4_BUFFER_TX); + assert_eq!(socket_4.rx_buffer, SOCKET4_BUFFER_RX); + } + + // Socket 5 + { + let socket_5 = Socket::new(5); + + assert_eq!(socket_5.register, SOCKET5); + assert_eq!(socket_5.tx_buffer, SOCKET5_BUFFER_TX); + assert_eq!(socket_5.rx_buffer, SOCKET5_BUFFER_RX); + } + + // Socket 6 + { + let socket_6 = Socket::new(6); + + assert_eq!(socket_6.register, SOCKET6); + assert_eq!(socket_6.tx_buffer, SOCKET6_BUFFER_TX); + assert_eq!(socket_6.rx_buffer, SOCKET6_BUFFER_RX); + } + + // Socket 7 + { + let socket_7 = Socket::new(7); + + assert_eq!(socket_7.register, SOCKET7); + assert_eq!(socket_7.tx_buffer, SOCKET7_BUFFER_TX); + assert_eq!(socket_7.rx_buffer, SOCKET7_BUFFER_RX); + } + } +} \ No newline at end of file From 6ff773d5edf0f6f5ffa4c40d5ae3e8ffa6892584 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Wed, 12 Jul 2023 17:40:47 +0300 Subject: [PATCH 2/5] docs: document initialize_manual default gateway Signed-off-by: Lachezar Lechev --- src/uninitialized_device.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/uninitialized_device.rs b/src/uninitialized_device.rs index ae230a3..9624f62 100644 --- a/src/uninitialized_device.rs +++ b/src/uninitialized_device.rs @@ -58,6 +58,10 @@ impl UninitializedDevice { self.initialize_with_host(host, mode_options) } + + /// The gateway overrides the passed `ip` ([`Ip4Addr`]) to end with `.1`. + /// + /// E.g. `let ip = "192.168.0.201".parse::()` will become a device with a gateway `192.168.0.1`. pub fn initialize_manual( self, mac: MacAddress, From c6e5c58845912ab1a9ff35c13dabad27294b099e Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Wed, 12 Jul 2023 17:50:34 +0300 Subject: [PATCH 3/5] feat: get device state and more derives Signed-off-by: Lachezar Lechev --- src/device.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/device.rs b/src/device.rs index bf41a7c..7630c12 100644 --- a/src/device.rs +++ b/src/device.rs @@ -8,6 +8,8 @@ use crate::socket::Socket; use crate::uninitialized_device::UninitializedDevice; use crate::{register, MacAddress}; +#[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ResetError { SocketsNotReleased, Other(E), @@ -21,7 +23,7 @@ impl From for ResetError { #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(crate) struct DeviceState { +pub struct DeviceState { host: HostImpl, sockets: [u8; 1], } @@ -42,6 +44,10 @@ impl Device { } } + pub fn get_state(&self) -> &DeviceState { + &self.state + } + pub fn reset(mut self) -> Result, ResetError> { if self.state.sockets != [0b11111111] { Err(ResetError::SocketsNotReleased) From f1c6a7a97b1bf7bc12146918c1230eb1c7e42981 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Wed, 12 Jul 2023 17:51:49 +0300 Subject: [PATCH 4/5] docs: document registers socketn Signed-off-by: Lachezar Lechev --- src/register.rs | 163 +++++++++++++++++++++++++++++++++++- src/socket.rs | 2 +- src/uninitialized_device.rs | 1 - 3 files changed, 160 insertions(+), 6 deletions(-) diff --git a/src/register.rs b/src/register.rs index d1bfef2..c9074ac 100644 --- a/src/register.rs +++ b/src/register.rs @@ -156,7 +156,10 @@ pub const SOCKET7_BUFFER_RX: u8 = 0b000_11111; pub mod socketn { use derive_try_from_primitive::TryFromPrimitive; + /// The Protocol mode pub const MODE: u16 = 0x00; + + /// The protocol modes that can be used with the `w5500` #[repr(u8)] pub enum Protocol { Closed = 0b00, @@ -164,33 +167,102 @@ pub mod socketn { Udp = 0b10, MacRaw = 0b100, } + + /// Socket n Command Register + /// + /// `Sn_CR` + // pub const COMMAND: u16 = 0b0001; pub const COMMAND: u16 = 0x01; + + /// Socket n Commands + /// + /// `Sn_CR` register #[repr(u8)] pub enum Command { Open = 0x01, + /// [Datasheet page 46](https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf): + /// + /// > This is valid only in TCP mode (Sn_MR(P3:P0) = Sn_MR_TCP). In this + /// > mode, Socket n operates as a ‘TCP server’ and waits for connection- + /// > request (SYN packet) from any ‘TCP client Listen = 0x02, Connect = 0x04, Discon = 0x08, Close = 0x10, Send = 0x20, + + /// [Datasheet page 48](https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf): + /// + /// > RECV completes the processing of the received data in Socket n RX + /// > Buffer by using a RX read pointer register (Sn_RX_RD). + /// > For more details, refer to Socket n RX Received Size Register + /// > (Sn_RX_RSR), Socket n RX Write Pointer Register (Sn_RX_WR), and + /// > Socket n RX Read Pointer Register (Sn_RX_RD). Receive = 0x40, } pub const INTERRUPT: u16 = 0x02; + /// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + /// | Reserved | Reserved | Reserved | SEND_OK | TIMEOUT | RECV | DISCON | CON | + /// + /// | Bit | Symbol | Description | + /// | 7~5 | Reserved | Reserved | + /// | 4 | SENDOK | Sn_IR(SENDOK) | Interrupt Mask | + /// | 3 | TIMEOUT | Sn_IR(TIMEOUT) | Interrupt Mask | + /// | 2 | RECV | Sn_IR(RECV) | Interrupt Mask | + /// | 1 | DISCON | Sn_IR(DISCON) | Interrupt Mask | + /// | 0 | CON | Sn_IR(CON) | Interrupt Mask | #[repr(u8)] pub enum Interrupt { All = 0b11111111u8, - SendOk = 0b010000u8, - Timeout = 0b01000u8, - Receive = 0b00100u8, + SendOk = 0b10000u8, + Timeout = 0b1000u8, + + /// Receive data + /// + /// bit 2, symbol `RECV`, `Sn_IR(RECV) Interrupt Mask` + Receive = 0b100u8, + + /// Disconnect + /// + /// bit 1, symbol `DISCON`, `Sn_IR(DISCON) Interrupt Mask` + Disconnect = 0b10u8, + + /// Connect + /// + /// bit 0, symbol `CON`, `Sn_IR(CON) Interrupt Mask` + Connect = 0b1u8, } pub const STATUS: u16 = 0x03; + + /// Socket status register + /// + /// `W5500 Datasheet Version 1.1.0` page 49: + /// + /// > Sn_SR (Socket n Status Register) [R] [0x0003] [0x00] + /// + /// - 0x18 SOCK_FIN_WAIT + /// - 0x1A SOCK_CLOSING + /// - 0X1B SOCK_TIME_WAIT + /// > These indicate Socket n is closing. + /// > These are shown in disconnect-process such as active-close + /// > and passive-close. + /// > When Disconnect-process is successfully completed, or + /// > when timeout occurs, these change to SOCK_CLOSED. + /// #[repr(u8)] - #[derive(TryFromPrimitive)] + #[derive(TryFromPrimitive, Debug, Copy, Clone, PartialEq, Eq)] + // #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Status { Closed = 0x00, Init = 0x13, + + /// [Datasheet page 49](https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf): + /// + /// > This indicates Socket n is operating as ‘TCP server’ mode and + /// > waiting for connection-request (SYN packet) from a peer + /// > (‘TCP client’). Listen = 0x14, Established = 0x17, CloseWait = 0x1c, @@ -206,6 +278,19 @@ pub mod socketn { LastAck = 0x1d, } + #[cfg(feature = "defmt")] + impl defmt::Format for Status { + fn format(&self, fmt: defmt::Formatter) { + // Format as hexadecimal. + defmt::write!( + fmt, + "Status::{} ({=u8:#x})", + defmt::Debug2Format(self), + *self as u8 + ); + } + } + pub const SOURCE_PORT: u16 = 0x04; pub const DESTINATION_IP: u16 = 0x0C; @@ -214,17 +299,87 @@ pub mod socketn { pub const RXBUF_SIZE: u16 = 0x1E; + /// Socket n TX Buffer Size Register + /// + /// `Sn_TXBUF_SIZE` + /// + /// From datasheet: + /// + /// > .. can be configured with 1,2,4,8, and 16 Kbytes. + /// > + /// > Although Socket n TX Buffer Block size is initially configured to 2Kbytes, user can + /// > be re-configure its size using Sn_TXBUF_SIZE. The total sum of Sn_TXBUF_SIZE + /// > cannot be exceed 16Kbytes. When exceeded, the data transmission error is + /// > occurred. pub const TXBUF_SIZE: u16 = 0x1F; + /// TX Free Size Register + /// + /// `Sn_TX_FSR` + /// + /// Socket n TX Free Size + /// + /// offset (register) + /// 0x0020 (Sn_TX_FSR0) + /// 0x0021 (Sn_TX_FSR1) pub const TX_FREE_SIZE: u16 = 0x20; + /// Socket n TX Read Pointer + /// + /// offset (register) + /// 0x0022 (Sn_TX_RD0) + /// 0x0023 (Sn_TX_RD1) pub const TX_DATA_READ_POINTER: u16 = 0x22; + /// Socket n TX Write Pointer + /// + /// offset (register) + /// 0x0024 (Sn_TX_WR0) + /// 0x0025 (Sn_TX_WR1) + /// + /// [Datasheet page 54](https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf): + /// + /// > Sn_TX_WR (Socket n TX Write Pointer Register) [R/W] [0x0024-0x0025] [0x0000] + /// > + /// > Sn_TX_WR is initialized by OPEN command. However, if Sn_MR(P[3:0]) is TCP + /// > mode(‘0001’), it is re-initialized while connecting with TCP. + /// > It should be read or to be updated like as follows. + /// > 1. Read the starting address for saving the transmitting data. + /// > 2. Save the transmitting data from the starting address of Socket n TX + /// > buffer. + /// > 3. After saving the transmitting data, update Sn_TX_WR to the + /// > increased value as many as transmitting data size. If the increment value + /// > exceeds the maximum value 0xFFFF(greater than 0x10000 and the carry + /// > bit occurs), then the carry bit is ignored and will automatically update + /// > with the lower 16bits value. + /// > 4. Transmit the saved data in Socket n TX Buffer by using SEND/SEND + /// command pub const TX_DATA_WRITE_POINTER: u16 = 0x24; + /// Socket n Received Size Register + /// + /// `Sn_RX_RSR` pub const RECEIVED_SIZE: u16 = 0x26; pub const RX_DATA_READ_POINTER: u16 = 0x28; + /// Socket n Interrupt Mask + /// + /// offset (register) + /// 0x002C (Sn_IMR) pub const INTERRUPT_MASK: u16 = 0x2C; + + #[cfg(test)] + mod tests { + use core::convert::TryFrom; + + use super::Status; + + #[test] + fn test_status_from_byte() { + let udp = 0x22_u8; + let status = Status::try_from(udp).expect("Should parse to Status"); + assert_eq!(status, Status::Udp); + } + } } diff --git a/src/socket.rs b/src/socket.rs index d275f64..98dc8a1 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -286,4 +286,4 @@ mod test { assert_eq!(socket_7.rx_buffer, SOCKET7_BUFFER_RX); } } -} \ No newline at end of file +} diff --git a/src/uninitialized_device.rs b/src/uninitialized_device.rs index 9624f62..8d18211 100644 --- a/src/uninitialized_device.rs +++ b/src/uninitialized_device.rs @@ -58,7 +58,6 @@ impl UninitializedDevice { self.initialize_with_host(host, mode_options) } - /// The gateway overrides the passed `ip` ([`Ip4Addr`]) to end with `.1`. /// /// E.g. `let ip = "192.168.0.201".parse::()` will become a device with a gateway `192.168.0.1`. From 02063d1c75e392f2de0d15963224da4462ad5766 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 25 Jul 2023 16:16:33 +0300 Subject: [PATCH 5/5] chore: PR review comments Signed-off-by: Lachezar Lechev --- src/register.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/register.rs b/src/register.rs index c9074ac..cf59cf4 100644 --- a/src/register.rs +++ b/src/register.rs @@ -171,7 +171,6 @@ pub mod socketn { /// Socket n Command Register /// /// `Sn_CR` - // pub const COMMAND: u16 = 0b0001; pub const COMMAND: u16 = 0x01; /// Socket n Commands @@ -251,9 +250,8 @@ pub mod socketn { /// > When Disconnect-process is successfully completed, or /// > when timeout occurs, these change to SOCK_CLOSED. /// - #[repr(u8)] #[derive(TryFromPrimitive, Debug, Copy, Clone, PartialEq, Eq)] - // #[cfg_attr(feature = "defmt", derive(defmt::Format))] + #[repr(u8)] pub enum Status { Closed = 0x00, Init = 0x13,