Add TCP-based NAL implementation (#24)
* Adding TCP-based NAL implementation * Updating NAL dependency * Updating connect to handle network errors * Adding support for getting IP and MAC, allowing transparent access to the W5500 in the interface. * Fixing TCP connect when not in INIT * Adding wait after reset * Reverting changes * More delta reductions * Fixing format * Updating changelog * Fixing docs * Updating NAL version * Adding debug derive * fixing TCP write * Updating RX receive size to allow less than 8 bytes * Fixing clippy lints
This commit is contained in:
parent
2f4603d0bc
commit
9327809fe7
9 changed files with 362 additions and 13 deletions
|
|
@ -1,3 +1,8 @@
|
||||||
|
# Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Added TCP client support
|
||||||
|
|
||||||
# 0.3.0 (June 10, 2020)
|
# 0.3.0 (June 10, 2020)
|
||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
[package]
|
[package]
|
||||||
name = "w5500"
|
name = "w5500"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
authors = ["Michael Watzko <michael@watzko.de>", "Jonah Dahlquist <hi@jonah.name>"]
|
authors = ["Michael Watzko <michael@watzko.de>", "Jonah Dahlquist <hi@jonah.name>", "Ryan Summers <ryan.summers@vertigo-designs.com"]
|
||||||
repository = "https://github.com/kellerkindt/w5500.git"
|
repository = "https://github.com/kellerkindt/w5500.git"
|
||||||
description = "W5500 IoT Controller implementation. Currently UDP sending and receiving is working. WIP"
|
description = "W5500 IoT Controller implementation."
|
||||||
keywords = ["embedded", "w5500", "iot", "arm", "embedded-hal-driver"]
|
keywords = ["embedded", "w5500", "iot", "arm", "embedded-hal-driver"]
|
||||||
categories = ["embedded", "hardware-support", "no-std", "network-programming"]
|
categories = ["embedded", "hardware-support", "no-std", "network-programming"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
@ -13,9 +13,10 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = { version = "1.3.4", default-features = false }
|
byteorder = { version = "1.3.4", default-features = false }
|
||||||
embedded-hal = "0.2.4"
|
embedded-hal = "0.2.4"
|
||||||
embedded-nal = "0.4.0"
|
embedded-nal = "0.6.0"
|
||||||
bit_field = "0.10.1"
|
bit_field = "0.10.1"
|
||||||
|
derive-try-from-primitive = "1"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
no-chip-version-assertion = []
|
no-chip-version-assertion = []
|
||||||
|
|
|
||||||
|
|
@ -77,5 +77,5 @@ Usage of borrowed SPI-Bus and previously initialized `Device`:
|
||||||
|
|
||||||
In no particular order, things to do to improve this driver.
|
In no particular order, things to do to improve this driver.
|
||||||
|
|
||||||
* Add support for TCP
|
* Add support for TCP server implementations
|
||||||
* Add support for DHCP
|
* Add support for DHCP
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
#![deny(broken_intra_doc_links)]
|
#![deny(rustdoc::broken_intra_doc_links)]
|
||||||
|
|
||||||
pub mod bus;
|
pub mod bus;
|
||||||
mod device;
|
mod device;
|
||||||
|
|
@ -8,6 +8,7 @@ mod host;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
pub mod register;
|
pub mod register;
|
||||||
mod socket;
|
mod socket;
|
||||||
|
pub mod tcp;
|
||||||
pub mod udp;
|
pub mod udp;
|
||||||
mod uninitialized_device;
|
mod uninitialized_device;
|
||||||
|
|
||||||
|
|
@ -34,8 +35,8 @@ pub enum OnPingRequest {
|
||||||
Ignore = 0b00010000,
|
Ignore = 0b00010000,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use [TransmissionMode::PPoE] when talking
|
/// Use [ConnectionType::PPoE] when talking
|
||||||
/// to an ADSL modem. Otherwise use [TransmissionMode::Ethernet]
|
/// to an ADSL modem. Otherwise use [ConnectionType::Ethernet]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Copy, Clone, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, PartialOrd, PartialEq)]
|
||||||
pub enum ConnectionType {
|
pub enum ConnectionType {
|
||||||
|
|
|
||||||
|
|
@ -159,19 +159,25 @@ pub const SOCKET7_BUFFER_TX: u8 = 0b000_11110;
|
||||||
pub const SOCKET7_BUFFER_RX: u8 = 0b000_11111;
|
pub const SOCKET7_BUFFER_RX: u8 = 0b000_11111;
|
||||||
|
|
||||||
pub mod socketn {
|
pub mod socketn {
|
||||||
|
use derive_try_from_primitive::TryFromPrimitive;
|
||||||
|
|
||||||
pub const MODE: u16 = 0x00;
|
pub const MODE: u16 = 0x00;
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Protocol {
|
pub enum Protocol {
|
||||||
Udp = 0b10u8,
|
Closed = 0b00,
|
||||||
Closed = 0u8,
|
Tcp = 0b01,
|
||||||
|
Udp = 0b10,
|
||||||
}
|
}
|
||||||
pub const COMMAND: u16 = 0x01;
|
pub const COMMAND: u16 = 0x01;
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
Open = 0x01,
|
Open = 0x01,
|
||||||
|
Listen = 0x02,
|
||||||
|
Connect = 0x04,
|
||||||
|
Discon = 0x08,
|
||||||
|
Close = 0x10,
|
||||||
Send = 0x20,
|
Send = 0x20,
|
||||||
Receive = 0x40,
|
Receive = 0x40,
|
||||||
Close = 0x10,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const INTERRUPT: u16 = 0x02;
|
pub const INTERRUPT: u16 = 0x02;
|
||||||
|
|
@ -183,12 +189,35 @@ pub mod socketn {
|
||||||
Receive = 0b00100u8,
|
Receive = 0b00100u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const STATUS: u16 = 0x03;
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(TryFromPrimitive)]
|
||||||
|
pub enum Status {
|
||||||
|
Closed = 0x00,
|
||||||
|
Init = 0x13,
|
||||||
|
Listen = 0x14,
|
||||||
|
Established = 0x17,
|
||||||
|
CloseWait = 0x1c,
|
||||||
|
Udp = 0x22,
|
||||||
|
MacRaw = 0x42,
|
||||||
|
|
||||||
|
// Transient states.
|
||||||
|
SynSent = 0x15,
|
||||||
|
SynRecv = 0x16,
|
||||||
|
FinWait = 0x18,
|
||||||
|
Closing = 0x1a,
|
||||||
|
TimeWait = 0x1b,
|
||||||
|
LastAck = 0x1d,
|
||||||
|
}
|
||||||
|
|
||||||
pub const SOURCE_PORT: u16 = 0x04;
|
pub const SOURCE_PORT: u16 = 0x04;
|
||||||
|
|
||||||
pub const DESTINATION_IP: u16 = 0x0C;
|
pub const DESTINATION_IP: u16 = 0x0C;
|
||||||
|
|
||||||
pub const DESTINATION_PORT: u16 = 0x10;
|
pub const DESTINATION_PORT: u16 = 0x10;
|
||||||
|
|
||||||
|
pub const TX_FREE_SIZE: u16 = 0x20;
|
||||||
|
|
||||||
pub const TX_DATA_READ_POINTER: u16 = 0x22;
|
pub const TX_DATA_READ_POINTER: u16 = 0x22;
|
||||||
|
|
||||||
pub const TX_DATA_WRITE_POINTER: u16 = 0x24;
|
pub const TX_DATA_WRITE_POINTER: u16 = 0x24;
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,12 @@ impl Socket {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_status<SpiBus: Bus>(&self, bus: &mut SpiBus) -> Result<u8, SpiBus::Error> {
|
||||||
|
let mut data = [0u8];
|
||||||
|
bus.read_frame(self.register(), socketn::STATUS, &mut data)?;
|
||||||
|
Ok(data[0])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reset_interrupt<SpiBus: Bus>(
|
pub fn reset_interrupt<SpiBus: Bus>(
|
||||||
&self,
|
&self,
|
||||||
bus: &mut SpiBus,
|
bus: &mut SpiBus,
|
||||||
|
|
@ -178,9 +184,15 @@ impl Socket {
|
||||||
bus.read_frame(self.register(), socketn::RECEIVED_SIZE, &mut sample_0)?;
|
bus.read_frame(self.register(), socketn::RECEIVED_SIZE, &mut sample_0)?;
|
||||||
let mut sample_1 = [0u8; 2];
|
let mut sample_1 = [0u8; 2];
|
||||||
bus.read_frame(self.register(), socketn::RECEIVED_SIZE, &mut sample_1)?;
|
bus.read_frame(self.register(), socketn::RECEIVED_SIZE, &mut sample_1)?;
|
||||||
if sample_0 == sample_1 && sample_0[0] >= 8 {
|
if sample_0 == sample_1 {
|
||||||
break Ok(u16::from_be_bytes(sample_0));
|
break Ok(u16::from_be_bytes(sample_0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_tx_free_size<SpiBus: Bus>(&self, bus: &mut SpiBus) -> Result<u16, SpiBus::Error> {
|
||||||
|
let mut data = [0; 2];
|
||||||
|
bus.read_frame(self.register(), socketn::TX_FREE_SIZE, &mut data)?;
|
||||||
|
Ok(u16::from_be_bytes(data))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
300
src/tcp.rs
Normal file
300
src/tcp.rs
Normal file
|
|
@ -0,0 +1,300 @@
|
||||||
|
use crate::{
|
||||||
|
bus::Bus,
|
||||||
|
device::{Device, DeviceRefMut},
|
||||||
|
host::Host,
|
||||||
|
register::socketn,
|
||||||
|
socket::Socket,
|
||||||
|
};
|
||||||
|
|
||||||
|
use embedded_nal::{nb, IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4, TcpClientStack};
|
||||||
|
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TcpSocketError<E: core::fmt::Debug> {
|
||||||
|
NoMoreSockets,
|
||||||
|
NotReady,
|
||||||
|
UnsupportedAddress,
|
||||||
|
Other(E),
|
||||||
|
UnsupportedMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: core::fmt::Debug> From<E> for TcpSocketError<E> {
|
||||||
|
fn from(e: E) -> Self {
|
||||||
|
TcpSocketError::Other(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TcpSocket {
|
||||||
|
socket: Socket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TcpSocket {
|
||||||
|
fn reopen<B: Bus>(&mut self, bus: &mut B) -> Result<(), TcpSocketError<B::Error>> {
|
||||||
|
self.socket.command(bus, socketn::Command::Close)?;
|
||||||
|
self.socket.reset_interrupt(bus, socketn::Interrupt::All)?;
|
||||||
|
self.socket.set_mode(bus, socketn::Protocol::Tcp)?;
|
||||||
|
|
||||||
|
self.socket.set_interrupt_mask(
|
||||||
|
bus,
|
||||||
|
socketn::Interrupt::SendOk as u8 & socketn::Interrupt::Timeout as u8,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.socket.command(bus, socketn::Command::Open)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn open<B: Bus>(
|
||||||
|
&mut self,
|
||||||
|
bus: &mut B,
|
||||||
|
local_port: u16,
|
||||||
|
) -> Result<(), TcpSocketError<B::Error>> {
|
||||||
|
self.socket.command(bus, socketn::Command::Close)?;
|
||||||
|
self.socket.reset_interrupt(bus, socketn::Interrupt::All)?;
|
||||||
|
self.socket.set_source_port(bus, local_port)?;
|
||||||
|
self.socket.set_mode(bus, socketn::Protocol::Tcp)?;
|
||||||
|
|
||||||
|
self.socket.set_interrupt_mask(
|
||||||
|
bus,
|
||||||
|
socketn::Interrupt::SendOk as u8 & socketn::Interrupt::Timeout as u8,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.socket.command(bus, socketn::Command::Open)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close<B: Bus>(&self, bus: &mut B) -> Result<(), TcpSocketError<B::Error>> {
|
||||||
|
self.socket.set_mode(bus, socketn::Protocol::Closed)?;
|
||||||
|
self.socket.command(bus, socketn::Command::Close)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connect<B: Bus>(
|
||||||
|
&mut self,
|
||||||
|
bus: &mut B,
|
||||||
|
remote: SocketAddrV4,
|
||||||
|
) -> Result<(), TcpSocketError<B::Error>> {
|
||||||
|
// Ensure the socket is open and ready before we attempt to connect it.
|
||||||
|
match socketn::Status::try_from(self.socket.get_status(bus)?) {
|
||||||
|
// Happy case: Nothing to do.
|
||||||
|
Ok(socketn::Status::Init) => {}
|
||||||
|
|
||||||
|
// If the socket is in the wrong mode, we can't use it. The user needs to re-open it.
|
||||||
|
Err(_) | Ok(socketn::Status::MacRaw) | Ok(socketn::Status::Udp) => {
|
||||||
|
return Err(TcpSocketError::UnsupportedMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other cases are transient TCP states. For these, we need to reset the TCP
|
||||||
|
// machinery to return to the INIT state.
|
||||||
|
Ok(_) => {
|
||||||
|
self.close(bus)?;
|
||||||
|
self.reopen(bus)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the remote port and IP
|
||||||
|
self.socket.set_destination_ip(bus, *remote.ip())?;
|
||||||
|
self.socket.set_destination_port(bus, remote.port())?;
|
||||||
|
|
||||||
|
// Connect the socket.
|
||||||
|
self.socket.command(bus, socketn::Command::Connect)?;
|
||||||
|
|
||||||
|
// Wait for the socket to connect or encounter an error.
|
||||||
|
loop {
|
||||||
|
match socketn::Status::try_from(self.socket.get_status(bus)?) {
|
||||||
|
Ok(socketn::Status::Established) => return Ok(()),
|
||||||
|
|
||||||
|
// The socket is closed if a timeout (ARP or SYN-ACK) or if the TCP socket receives
|
||||||
|
// a RST packet. In this case, the client will need to re-attempt to connect.
|
||||||
|
|
||||||
|
// TODO: Due to limitations of the embedded-nal, we currently still return the
|
||||||
|
// socket (since we cannot inform the user of the connection failure). The returned
|
||||||
|
// socket will not actually be connected.
|
||||||
|
Ok(socketn::Status::Closed) => {
|
||||||
|
// For now, always return an open socket so that the user can re-connect with
|
||||||
|
// it in the future.
|
||||||
|
self.close(bus)?;
|
||||||
|
return self.reopen(bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The socket is still in some transient state. Wait for it to connect or for the
|
||||||
|
// connection to fail.
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_connected<B: Bus>(&self, bus: &mut B) -> Result<bool, TcpSocketError<B::Error>> {
|
||||||
|
Ok(self.socket.get_status(bus)? == socketn::Status::Established as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send<B: Bus>(
|
||||||
|
&mut self,
|
||||||
|
bus: &mut B,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<usize, TcpSocketError<B::Error>> {
|
||||||
|
if !self.is_connected(bus)? {
|
||||||
|
return Err(TcpSocketError::NotReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_size = self.socket.get_tx_free_size(bus)? as usize;
|
||||||
|
|
||||||
|
let write_data = if data.len() < max_size {
|
||||||
|
data
|
||||||
|
} else {
|
||||||
|
&data[..max_size]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Append the data to the write buffer after the current write pointer.
|
||||||
|
let write_pointer = self.socket.get_tx_write_pointer(bus)?;
|
||||||
|
|
||||||
|
// Write data into the buffer and update the writer pointer.
|
||||||
|
bus.write_frame(self.socket.tx_buffer(), write_pointer, write_data)?;
|
||||||
|
self.socket
|
||||||
|
.set_tx_write_pointer(bus, write_pointer.wrapping_add(write_data.len() as u16))?;
|
||||||
|
|
||||||
|
// Send the data.
|
||||||
|
self.socket.command(bus, socketn::Command::Send)?;
|
||||||
|
|
||||||
|
// Wait until the send command completes.
|
||||||
|
while !self.socket.has_interrupt(bus, socketn::Interrupt::SendOk)? {}
|
||||||
|
self.socket
|
||||||
|
.reset_interrupt(bus, socketn::Interrupt::SendOk)?;
|
||||||
|
|
||||||
|
Ok(write_data.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive<B: Bus>(
|
||||||
|
&mut self,
|
||||||
|
bus: &mut B,
|
||||||
|
data: &mut [u8],
|
||||||
|
) -> Result<usize, TcpSocketError<B::Error>> {
|
||||||
|
if !self.is_connected(bus)? {
|
||||||
|
return Err(TcpSocketError::NotReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we've received data.
|
||||||
|
if !self
|
||||||
|
.socket
|
||||||
|
.has_interrupt(bus, socketn::Interrupt::Receive)?
|
||||||
|
{
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rx_size = self.socket.get_receive_size(bus)? as usize;
|
||||||
|
|
||||||
|
let read_buffer = if rx_size > data.len() {
|
||||||
|
data
|
||||||
|
} else {
|
||||||
|
&mut data[..rx_size]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read from the RX ring buffer.
|
||||||
|
let read_pointer = self.socket.get_rx_read_pointer(bus)?;
|
||||||
|
bus.read_frame(self.socket.rx_buffer(), read_pointer, read_buffer)?;
|
||||||
|
self.socket
|
||||||
|
.set_rx_read_pointer(bus, read_pointer.wrapping_add(read_buffer.len() as u16))?;
|
||||||
|
|
||||||
|
// Register the reception as complete.
|
||||||
|
self.socket.command(bus, socketn::Command::Receive)?;
|
||||||
|
self.socket
|
||||||
|
.reset_interrupt(bus, socketn::Interrupt::Receive)?;
|
||||||
|
|
||||||
|
Ok(read_buffer.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SpiBus: Bus, HostImpl: Host> TcpClientStack for DeviceRefMut<'_, SpiBus, HostImpl> {
|
||||||
|
type TcpSocket = TcpSocket;
|
||||||
|
type Error = TcpSocketError<SpiBus::Error>;
|
||||||
|
|
||||||
|
fn socket(&mut self) -> Result<TcpSocket, Self::Error> {
|
||||||
|
match self.take_socket() {
|
||||||
|
Some(socket) => Ok(TcpSocket { socket }),
|
||||||
|
None => Err(TcpSocketError::NoMoreSockets),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connect(
|
||||||
|
&mut self,
|
||||||
|
socket: &mut Self::TcpSocket,
|
||||||
|
remote: SocketAddr,
|
||||||
|
) -> nb::Result<(), Self::Error> {
|
||||||
|
if let SocketAddr::V4(remote) = remote {
|
||||||
|
// TODO dynamically select a random port
|
||||||
|
socket.open(&mut self.bus, 49849 + u16::from(socket.socket.index))?; // chosen by fair dice roll.
|
||||||
|
socket.connect(&mut self.bus, remote)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(nb::Error::Other(Self::Error::UnsupportedAddress))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_connected(&mut self, socket: &Self::TcpSocket) -> Result<bool, Self::Error> {
|
||||||
|
socket.is_connected(&mut self.bus)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(
|
||||||
|
&mut self,
|
||||||
|
socket: &mut Self::TcpSocket,
|
||||||
|
buffer: &[u8],
|
||||||
|
) -> nb::Result<usize, Self::Error> {
|
||||||
|
let len = socket.send(&mut self.bus, buffer)?;
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive(
|
||||||
|
&mut self,
|
||||||
|
socket: &mut Self::TcpSocket,
|
||||||
|
buffer: &mut [u8],
|
||||||
|
) -> nb::Result<usize, Self::Error> {
|
||||||
|
Ok(socket.receive(&mut self.bus, buffer)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, socket: Self::TcpSocket) -> Result<(), Self::Error> {
|
||||||
|
socket.close(&mut self.bus)?;
|
||||||
|
self.release_socket(socket.socket);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SpiBus: Bus, HostImpl: Host> TcpClientStack for Device<SpiBus, HostImpl> {
|
||||||
|
type TcpSocket = TcpSocket;
|
||||||
|
type Error = TcpSocketError<SpiBus::Error>;
|
||||||
|
|
||||||
|
fn socket(&mut self) -> Result<TcpSocket, Self::Error> {
|
||||||
|
self.as_mut().socket()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connect(
|
||||||
|
&mut self,
|
||||||
|
socket: &mut Self::TcpSocket,
|
||||||
|
remote: SocketAddr,
|
||||||
|
) -> nb::Result<(), Self::Error> {
|
||||||
|
self.as_mut().connect(socket, remote)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_connected(&mut self, socket: &Self::TcpSocket) -> Result<bool, Self::Error> {
|
||||||
|
self.as_mut().is_connected(socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(
|
||||||
|
&mut self,
|
||||||
|
socket: &mut Self::TcpSocket,
|
||||||
|
buffer: &[u8],
|
||||||
|
) -> nb::Result<usize, Self::Error> {
|
||||||
|
self.as_mut().send(socket, buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive(
|
||||||
|
&mut self,
|
||||||
|
socket: &mut Self::TcpSocket,
|
||||||
|
buffer: &mut [u8],
|
||||||
|
) -> nb::Result<usize, Self::Error> {
|
||||||
|
self.as_mut().receive(socket, buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, socket: Self::TcpSocket) -> Result<(), Self::Error> {
|
||||||
|
self.as_mut().close(socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -51,7 +51,7 @@ impl UdpSocket {
|
||||||
// TODO ensure write is currently possible
|
// TODO ensure write is currently possible
|
||||||
self.socket
|
self.socket
|
||||||
.set_tx_read_pointer(bus, 0)
|
.set_tx_read_pointer(bus, 0)
|
||||||
.and_then(|_| bus.write_frame(self.socket.tx_buffer(), 0, &send_buffer))
|
.and_then(|_| bus.write_frame(self.socket.tx_buffer(), 0, send_buffer))
|
||||||
.and_then(|_| {
|
.and_then(|_| {
|
||||||
self.socket
|
self.socket
|
||||||
.set_tx_write_pointer(bus, send_buffer.len() as u16)
|
.set_tx_write_pointer(bus, send_buffer.len() as u16)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ pub struct UninitializedDevice<SpiBus: Bus> {
|
||||||
bus: SpiBus,
|
bus: SpiBus,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum InitializeError<SpiError> {
|
pub enum InitializeError<SpiError> {
|
||||||
SpiError(SpiError),
|
SpiError(SpiError),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue