Merge pull request #771 from embassy-rs/net-split
net: add split() to tcpsocket
This commit is contained in:
		
						commit
						d0fe9af458
					
				| @ -31,15 +31,13 @@ pool-32 = [] | |||||||
| pool-64 = [] | pool-64 = [] | ||||||
| pool-128 = [] | pool-128 = [] | ||||||
| 
 | 
 | ||||||
| nightly = ["embedded-io/async"] |  | ||||||
| 
 |  | ||||||
| [dependencies] | [dependencies] | ||||||
| 
 | 
 | ||||||
| defmt = { version = "0.3", optional = true } | defmt = { version = "0.3", optional = true } | ||||||
| log = { version = "0.4.14", optional = true } | log = { version = "0.4.14", optional = true } | ||||||
| 
 | 
 | ||||||
| embassy             = { version = "0.1.0", path = "../embassy" } | embassy             = { version = "0.1.0", path = "../embassy" } | ||||||
| embedded-io = "0.3.0" | embedded-io         = { version = "0.3.0", features = [ "async" ] } | ||||||
| 
 | 
 | ||||||
| managed             = { version = "0.8.0", default-features = false, features = [ "map" ] } | managed             = { version = "0.8.0", default-features = false, features = [ "map" ] } | ||||||
| heapless            = { version = "0.7.5", default-features = false } | heapless            = { version = "0.7.5", default-features = false } | ||||||
|  | |||||||
| @ -1,9 +1,6 @@ | |||||||
| #![cfg_attr(not(feature = "std"), no_std)] | #![cfg_attr(not(feature = "std"), no_std)] | ||||||
| #![allow(clippy::new_without_default)] | #![allow(clippy::new_without_default)] | ||||||
| #![cfg_attr(
 | #![feature(generic_associated_types, type_alias_impl_trait)] | ||||||
|     feature = "nightly", |  | ||||||
|     feature(generic_associated_types, type_alias_impl_trait) |  | ||||||
| )] |  | ||||||
| 
 | 
 | ||||||
| // This mod MUST go first, so that the others see its macros.
 | // This mod MUST go first, so that the others see its macros.
 | ||||||
| pub(crate) mod fmt; | pub(crate) mod fmt; | ||||||
|  | |||||||
							
								
								
									
										353
									
								
								embassy-net/src/tcp.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								embassy-net/src/tcp.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,353 @@ | |||||||
|  | use core::future::Future; | ||||||
|  | use core::marker::PhantomData; | ||||||
|  | use core::mem; | ||||||
|  | use core::task::Poll; | ||||||
|  | use futures::future::poll_fn; | ||||||
|  | use smoltcp::iface::{Context as SmolContext, SocketHandle}; | ||||||
|  | use smoltcp::socket::TcpSocket as SyncTcpSocket; | ||||||
|  | use smoltcp::socket::{TcpSocketBuffer, TcpState}; | ||||||
|  | use smoltcp::time::Duration; | ||||||
|  | use smoltcp::wire::IpEndpoint; | ||||||
|  | 
 | ||||||
|  | use super::stack::Stack; | ||||||
|  | 
 | ||||||
|  | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||||||
|  | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||||
|  | pub enum Error { | ||||||
|  |     ConnectionReset, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||||||
|  | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||||
|  | pub enum ConnectError { | ||||||
|  |     /// The socket is already connected or listening.
 | ||||||
|  |     InvalidState, | ||||||
|  |     /// The remote host rejected the connection with a RST packet.
 | ||||||
|  |     ConnectionReset, | ||||||
|  |     /// Connect timed out.
 | ||||||
|  |     TimedOut, | ||||||
|  |     /// No route to host.
 | ||||||
|  |     NoRoute, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||||||
|  | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||||
|  | pub enum AcceptError { | ||||||
|  |     /// The socket is already connected or listening.
 | ||||||
|  |     InvalidState, | ||||||
|  |     /// Invalid listen port
 | ||||||
|  |     InvalidPort, | ||||||
|  |     /// The remote host rejected the connection with a RST packet.
 | ||||||
|  |     ConnectionReset, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct TcpSocket<'a> { | ||||||
|  |     handle: SocketHandle, | ||||||
|  |     ghost: PhantomData<&'a mut [u8]>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> Unpin for TcpSocket<'a> {} | ||||||
|  | 
 | ||||||
|  | pub struct TcpReader<'a> { | ||||||
|  |     handle: SocketHandle, | ||||||
|  |     ghost: PhantomData<&'a mut [u8]>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> Unpin for TcpReader<'a> {} | ||||||
|  | 
 | ||||||
|  | pub struct TcpWriter<'a> { | ||||||
|  |     handle: SocketHandle, | ||||||
|  |     ghost: PhantomData<&'a mut [u8]>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> Unpin for TcpWriter<'a> {} | ||||||
|  | 
 | ||||||
|  | impl<'a> TcpSocket<'a> { | ||||||
|  |     pub fn new(rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { | ||||||
|  |         let handle = Stack::with(|stack| { | ||||||
|  |             let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; | ||||||
|  |             let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; | ||||||
|  |             stack.iface.add_socket(SyncTcpSocket::new( | ||||||
|  |                 TcpSocketBuffer::new(rx_buffer), | ||||||
|  |                 TcpSocketBuffer::new(tx_buffer), | ||||||
|  |             )) | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         Self { | ||||||
|  |             handle, | ||||||
|  |             ghost: PhantomData, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { | ||||||
|  |         ( | ||||||
|  |             TcpReader { | ||||||
|  |                 handle: self.handle, | ||||||
|  |                 ghost: PhantomData, | ||||||
|  |             }, | ||||||
|  |             TcpWriter { | ||||||
|  |                 handle: self.handle, | ||||||
|  |                 ghost: PhantomData, | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError> | ||||||
|  |     where | ||||||
|  |         T: Into<IpEndpoint>, | ||||||
|  |     { | ||||||
|  |         let local_port = Stack::with(|stack| stack.get_local_port()); | ||||||
|  |         match with_socket(self.handle, |s, cx| { | ||||||
|  |             s.connect(cx, remote_endpoint, local_port) | ||||||
|  |         }) { | ||||||
|  |             Ok(()) => {} | ||||||
|  |             Err(smoltcp::Error::Illegal) => return Err(ConnectError::InvalidState), | ||||||
|  |             Err(smoltcp::Error::Unaddressable) => return Err(ConnectError::NoRoute), | ||||||
|  |             // smoltcp returns no errors other than the above.
 | ||||||
|  |             Err(_) => unreachable!(), | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         futures::future::poll_fn(|cx| { | ||||||
|  |             with_socket(self.handle, |s, _| match s.state() { | ||||||
|  |                 TcpState::Closed | TcpState::TimeWait => { | ||||||
|  |                     Poll::Ready(Err(ConnectError::ConnectionReset)) | ||||||
|  |                 } | ||||||
|  |                 TcpState::Listen => unreachable!(), | ||||||
|  |                 TcpState::SynSent | TcpState::SynReceived => { | ||||||
|  |                     s.register_send_waker(cx.waker()); | ||||||
|  |                     Poll::Pending | ||||||
|  |                 } | ||||||
|  |                 _ => Poll::Ready(Ok(())), | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |         .await | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError> | ||||||
|  |     where | ||||||
|  |         T: Into<IpEndpoint>, | ||||||
|  |     { | ||||||
|  |         match with_socket(self.handle, |s, _| s.listen(local_endpoint)) { | ||||||
|  |             Ok(()) => {} | ||||||
|  |             Err(smoltcp::Error::Illegal) => return Err(AcceptError::InvalidState), | ||||||
|  |             Err(smoltcp::Error::Unaddressable) => return Err(AcceptError::InvalidPort), | ||||||
|  |             // smoltcp returns no errors other than the above.
 | ||||||
|  |             Err(_) => unreachable!(), | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         futures::future::poll_fn(|cx| { | ||||||
|  |             with_socket(self.handle, |s, _| match s.state() { | ||||||
|  |                 TcpState::Listen | TcpState::SynSent | TcpState::SynReceived => { | ||||||
|  |                     s.register_send_waker(cx.waker()); | ||||||
|  |                     Poll::Pending | ||||||
|  |                 } | ||||||
|  |                 _ => Poll::Ready(Ok(())), | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |         .await | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_timeout(&mut self, duration: Option<Duration>) { | ||||||
|  |         with_socket(self.handle, |s, _| s.set_timeout(duration)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_keep_alive(&mut self, interval: Option<Duration>) { | ||||||
|  |         with_socket(self.handle, |s, _| s.set_keep_alive(interval)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) { | ||||||
|  |         with_socket(self.handle, |s, _| s.set_hop_limit(hop_limit)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn local_endpoint(&self) -> IpEndpoint { | ||||||
|  |         with_socket(self.handle, |s, _| s.local_endpoint()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn remote_endpoint(&self) -> IpEndpoint { | ||||||
|  |         with_socket(self.handle, |s, _| s.remote_endpoint()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn state(&self) -> TcpState { | ||||||
|  |         with_socket(self.handle, |s, _| s.state()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn close(&mut self) { | ||||||
|  |         with_socket(self.handle, |s, _| s.close()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn abort(&mut self) { | ||||||
|  |         with_socket(self.handle, |s, _| s.abort()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn may_send(&self) -> bool { | ||||||
|  |         with_socket(self.handle, |s, _| s.may_send()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn may_recv(&self) -> bool { | ||||||
|  |         with_socket(self.handle, |s, _| s.may_recv()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn with_socket<R>( | ||||||
|  |     handle: SocketHandle, | ||||||
|  |     f: impl FnOnce(&mut SyncTcpSocket, &mut SmolContext) -> R, | ||||||
|  | ) -> R { | ||||||
|  |     Stack::with(|stack| { | ||||||
|  |         let res = { | ||||||
|  |             let (s, cx) = stack.iface.get_socket_and_context::<SyncTcpSocket>(handle); | ||||||
|  |             f(s, cx) | ||||||
|  |         }; | ||||||
|  |         stack.wake(); | ||||||
|  |         res | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> Drop for TcpSocket<'a> { | ||||||
|  |     fn drop(&mut self) { | ||||||
|  |         Stack::with(|stack| { | ||||||
|  |             stack.iface.remove_socket(self.handle); | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl embedded_io::Error for Error { | ||||||
|  |     fn kind(&self) -> embedded_io::ErrorKind { | ||||||
|  |         embedded_io::ErrorKind::Other | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'d> embedded_io::Io for TcpSocket<'d> { | ||||||
|  |     type Error = Error; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { | ||||||
|  |     type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||||||
|  |         poll_fn(move |cx| { | ||||||
|  |             // CAUTION: smoltcp semantics around EOF are different to what you'd expect
 | ||||||
|  |             // from posix-like IO, so we have to tweak things here.
 | ||||||
|  |             with_socket(self.handle, |s, _| match s.recv_slice(buf) { | ||||||
|  |                 // No data ready
 | ||||||
|  |                 Ok(0) => { | ||||||
|  |                     s.register_recv_waker(cx.waker()); | ||||||
|  |                     Poll::Pending | ||||||
|  |                 } | ||||||
|  |                 // Data ready!
 | ||||||
|  |                 Ok(n) => Poll::Ready(Ok(n)), | ||||||
|  |                 // EOF
 | ||||||
|  |                 Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)), | ||||||
|  |                 // Connection reset. TODO: this can also be timeouts etc, investigate.
 | ||||||
|  |                 Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||||||
|  |                 // smoltcp returns no errors other than the above.
 | ||||||
|  |                 Err(_) => unreachable!(), | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { | ||||||
|  |     type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||||||
|  |         poll_fn(move |cx| { | ||||||
|  |             with_socket(self.handle, |s, _| match s.send_slice(buf) { | ||||||
|  |                 // Not ready to send (no space in the tx buffer)
 | ||||||
|  |                 Ok(0) => { | ||||||
|  |                     s.register_send_waker(cx.waker()); | ||||||
|  |                     Poll::Pending | ||||||
|  |                 } | ||||||
|  |                 // Some data sent
 | ||||||
|  |                 Ok(n) => Poll::Ready(Ok(n)), | ||||||
|  |                 // Connection reset. TODO: this can also be timeouts etc, investigate.
 | ||||||
|  |                 Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||||||
|  |                 // smoltcp returns no errors other than the above.
 | ||||||
|  |                 Err(_) => unreachable!(), | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { | ||||||
|  |         poll_fn(move |_| { | ||||||
|  |             Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
 | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'d> embedded_io::Io for TcpReader<'d> { | ||||||
|  |     type Error = Error; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'d> embedded_io::asynch::Read for TcpReader<'d> { | ||||||
|  |     type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||||||
|  |         poll_fn(move |cx| { | ||||||
|  |             // CAUTION: smoltcp semantics around EOF are different to what you'd expect
 | ||||||
|  |             // from posix-like IO, so we have to tweak things here.
 | ||||||
|  |             with_socket(self.handle, |s, _| match s.recv_slice(buf) { | ||||||
|  |                 // No data ready
 | ||||||
|  |                 Ok(0) => { | ||||||
|  |                     s.register_recv_waker(cx.waker()); | ||||||
|  |                     Poll::Pending | ||||||
|  |                 } | ||||||
|  |                 // Data ready!
 | ||||||
|  |                 Ok(n) => Poll::Ready(Ok(n)), | ||||||
|  |                 // EOF
 | ||||||
|  |                 Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)), | ||||||
|  |                 // Connection reset. TODO: this can also be timeouts etc, investigate.
 | ||||||
|  |                 Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||||||
|  |                 // smoltcp returns no errors other than the above.
 | ||||||
|  |                 Err(_) => unreachable!(), | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'d> embedded_io::Io for TcpWriter<'d> { | ||||||
|  |     type Error = Error; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { | ||||||
|  |     type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||||||
|  |         poll_fn(move |cx| { | ||||||
|  |             with_socket(self.handle, |s, _| match s.send_slice(buf) { | ||||||
|  |                 // Not ready to send (no space in the tx buffer)
 | ||||||
|  |                 Ok(0) => { | ||||||
|  |                     s.register_send_waker(cx.waker()); | ||||||
|  |                     Poll::Pending | ||||||
|  |                 } | ||||||
|  |                 // Some data sent
 | ||||||
|  |                 Ok(n) => Poll::Ready(Ok(n)), | ||||||
|  |                 // Connection reset. TODO: this can also be timeouts etc, investigate.
 | ||||||
|  |                 Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||||||
|  |                 // smoltcp returns no errors other than the above.
 | ||||||
|  |                 Err(_) => unreachable!(), | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { | ||||||
|  |         poll_fn(move |_| { | ||||||
|  |             Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
 | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,67 +0,0 @@ | |||||||
| use core::future::Future; |  | ||||||
| use core::task::Poll; |  | ||||||
| use futures::future::poll_fn; |  | ||||||
| 
 |  | ||||||
| use super::{Error, TcpSocket}; |  | ||||||
| 
 |  | ||||||
| impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { |  | ||||||
|     type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> |  | ||||||
|     where |  | ||||||
|         Self: 'a; |  | ||||||
| 
 |  | ||||||
|     fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { |  | ||||||
|         poll_fn(move |cx| { |  | ||||||
|             // CAUTION: smoltcp semantics around EOF are different to what you'd expect
 |  | ||||||
|             // from posix-like IO, so we have to tweak things here.
 |  | ||||||
|             self.with(|s, _| match s.recv_slice(buf) { |  | ||||||
|                 // No data ready
 |  | ||||||
|                 Ok(0) => { |  | ||||||
|                     s.register_recv_waker(cx.waker()); |  | ||||||
|                     Poll::Pending |  | ||||||
|                 } |  | ||||||
|                 // Data ready!
 |  | ||||||
|                 Ok(n) => Poll::Ready(Ok(n)), |  | ||||||
|                 // EOF
 |  | ||||||
|                 Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)), |  | ||||||
|                 // Connection reset. TODO: this can also be timeouts etc, investigate.
 |  | ||||||
|                 Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), |  | ||||||
|                 // smoltcp returns no errors other than the above.
 |  | ||||||
|                 Err(_) => unreachable!(), |  | ||||||
|             }) |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { |  | ||||||
|     type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> |  | ||||||
|     where |  | ||||||
|         Self: 'a; |  | ||||||
| 
 |  | ||||||
|     fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { |  | ||||||
|         poll_fn(move |cx| { |  | ||||||
|             self.with(|s, _| match s.send_slice(buf) { |  | ||||||
|                 // Not ready to send (no space in the tx buffer)
 |  | ||||||
|                 Ok(0) => { |  | ||||||
|                     s.register_send_waker(cx.waker()); |  | ||||||
|                     Poll::Pending |  | ||||||
|                 } |  | ||||||
|                 // Some data sent
 |  | ||||||
|                 Ok(n) => Poll::Ready(Ok(n)), |  | ||||||
|                 // Connection reset. TODO: this can also be timeouts etc, investigate.
 |  | ||||||
|                 Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), |  | ||||||
|                 // smoltcp returns no errors other than the above.
 |  | ||||||
|                 Err(_) => unreachable!(), |  | ||||||
|             }) |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> |  | ||||||
|     where |  | ||||||
|         Self: 'a; |  | ||||||
| 
 |  | ||||||
|     fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { |  | ||||||
|         poll_fn(move |_| { |  | ||||||
|             Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
 |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,192 +0,0 @@ | |||||||
| use core::marker::PhantomData; |  | ||||||
| use core::mem; |  | ||||||
| use core::task::Poll; |  | ||||||
| use smoltcp::iface::{Context as SmolContext, SocketHandle}; |  | ||||||
| use smoltcp::socket::TcpSocket as SyncTcpSocket; |  | ||||||
| use smoltcp::socket::{TcpSocketBuffer, TcpState}; |  | ||||||
| use smoltcp::time::Duration; |  | ||||||
| use smoltcp::wire::IpEndpoint; |  | ||||||
| 
 |  | ||||||
| #[cfg(feature = "nightly")] |  | ||||||
| mod io_impl; |  | ||||||
| 
 |  | ||||||
| use super::stack::Stack; |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Eq, Clone, Copy, Debug)] |  | ||||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] |  | ||||||
| pub enum Error { |  | ||||||
|     ConnectionReset, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Eq, Clone, Copy, Debug)] |  | ||||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] |  | ||||||
| pub enum ConnectError { |  | ||||||
|     /// The socket is already connected or listening.
 |  | ||||||
|     InvalidState, |  | ||||||
|     /// The remote host rejected the connection with a RST packet.
 |  | ||||||
|     ConnectionReset, |  | ||||||
|     /// Connect timed out.
 |  | ||||||
|     TimedOut, |  | ||||||
|     /// No route to host.
 |  | ||||||
|     NoRoute, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Eq, Clone, Copy, Debug)] |  | ||||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] |  | ||||||
| pub enum AcceptError { |  | ||||||
|     /// The socket is already connected or listening.
 |  | ||||||
|     InvalidState, |  | ||||||
|     /// Invalid listen port
 |  | ||||||
|     InvalidPort, |  | ||||||
|     /// The remote host rejected the connection with a RST packet.
 |  | ||||||
|     ConnectionReset, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct TcpSocket<'a> { |  | ||||||
|     handle: SocketHandle, |  | ||||||
|     ghost: PhantomData<&'a mut [u8]>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'a> Unpin for TcpSocket<'a> {} |  | ||||||
| 
 |  | ||||||
| impl<'a> TcpSocket<'a> { |  | ||||||
|     pub fn new(rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { |  | ||||||
|         let handle = Stack::with(|stack| { |  | ||||||
|             let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; |  | ||||||
|             let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; |  | ||||||
|             stack.iface.add_socket(SyncTcpSocket::new( |  | ||||||
|                 TcpSocketBuffer::new(rx_buffer), |  | ||||||
|                 TcpSocketBuffer::new(tx_buffer), |  | ||||||
|             )) |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         Self { |  | ||||||
|             handle, |  | ||||||
|             ghost: PhantomData, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError> |  | ||||||
|     where |  | ||||||
|         T: Into<IpEndpoint>, |  | ||||||
|     { |  | ||||||
|         let local_port = Stack::with(|stack| stack.get_local_port()); |  | ||||||
|         match self.with(|s, cx| s.connect(cx, remote_endpoint, local_port)) { |  | ||||||
|             Ok(()) => {} |  | ||||||
|             Err(smoltcp::Error::Illegal) => return Err(ConnectError::InvalidState), |  | ||||||
|             Err(smoltcp::Error::Unaddressable) => return Err(ConnectError::NoRoute), |  | ||||||
|             // smoltcp returns no errors other than the above.
 |  | ||||||
|             Err(_) => unreachable!(), |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         futures::future::poll_fn(|cx| { |  | ||||||
|             self.with(|s, _| match s.state() { |  | ||||||
|                 TcpState::Closed | TcpState::TimeWait => { |  | ||||||
|                     Poll::Ready(Err(ConnectError::ConnectionReset)) |  | ||||||
|                 } |  | ||||||
|                 TcpState::Listen => unreachable!(), |  | ||||||
|                 TcpState::SynSent | TcpState::SynReceived => { |  | ||||||
|                     s.register_send_waker(cx.waker()); |  | ||||||
|                     Poll::Pending |  | ||||||
|                 } |  | ||||||
|                 _ => Poll::Ready(Ok(())), |  | ||||||
|             }) |  | ||||||
|         }) |  | ||||||
|         .await |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError> |  | ||||||
|     where |  | ||||||
|         T: Into<IpEndpoint>, |  | ||||||
|     { |  | ||||||
|         match self.with(|s, _| s.listen(local_endpoint)) { |  | ||||||
|             Ok(()) => {} |  | ||||||
|             Err(smoltcp::Error::Illegal) => return Err(AcceptError::InvalidState), |  | ||||||
|             Err(smoltcp::Error::Unaddressable) => return Err(AcceptError::InvalidPort), |  | ||||||
|             // smoltcp returns no errors other than the above.
 |  | ||||||
|             Err(_) => unreachable!(), |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         futures::future::poll_fn(|cx| { |  | ||||||
|             self.with(|s, _| match s.state() { |  | ||||||
|                 TcpState::Listen | TcpState::SynSent | TcpState::SynReceived => { |  | ||||||
|                     s.register_send_waker(cx.waker()); |  | ||||||
|                     Poll::Pending |  | ||||||
|                 } |  | ||||||
|                 _ => Poll::Ready(Ok(())), |  | ||||||
|             }) |  | ||||||
|         }) |  | ||||||
|         .await |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn set_timeout(&mut self, duration: Option<Duration>) { |  | ||||||
|         self.with(|s, _| s.set_timeout(duration)) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn set_keep_alive(&mut self, interval: Option<Duration>) { |  | ||||||
|         self.with(|s, _| s.set_keep_alive(interval)) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) { |  | ||||||
|         self.with(|s, _| s.set_hop_limit(hop_limit)) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn local_endpoint(&self) -> IpEndpoint { |  | ||||||
|         self.with(|s, _| s.local_endpoint()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn remote_endpoint(&self) -> IpEndpoint { |  | ||||||
|         self.with(|s, _| s.remote_endpoint()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn state(&self) -> TcpState { |  | ||||||
|         self.with(|s, _| s.state()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn close(&mut self) { |  | ||||||
|         self.with(|s, _| s.close()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn abort(&mut self) { |  | ||||||
|         self.with(|s, _| s.abort()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn may_send(&self) -> bool { |  | ||||||
|         self.with(|s, _| s.may_send()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn may_recv(&self) -> bool { |  | ||||||
|         self.with(|s, _| s.may_recv()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn with<R>(&self, f: impl FnOnce(&mut SyncTcpSocket, &mut SmolContext) -> R) -> R { |  | ||||||
|         Stack::with(|stack| { |  | ||||||
|             let res = { |  | ||||||
|                 let (s, cx) = stack |  | ||||||
|                     .iface |  | ||||||
|                     .get_socket_and_context::<SyncTcpSocket>(self.handle); |  | ||||||
|                 f(s, cx) |  | ||||||
|             }; |  | ||||||
|             stack.wake(); |  | ||||||
|             res |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'a> Drop for TcpSocket<'a> { |  | ||||||
|     fn drop(&mut self) { |  | ||||||
|         Stack::with(|stack| { |  | ||||||
|             stack.iface.remove_socket(self.handle); |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl embedded_io::Error for Error { |  | ||||||
|     fn kind(&self) -> embedded_io::ErrorKind { |  | ||||||
|         embedded_io::ErrorKind::Other |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'d> embedded_io::Io for TcpSocket<'d> { |  | ||||||
|     type Error = Error; |  | ||||||
| } |  | ||||||
| @ -90,7 +90,7 @@ time-driver-tim12 = ["_time-driver"] | |||||||
| time-driver-tim15 = ["_time-driver"] | time-driver-tim15 = ["_time-driver"] | ||||||
| 
 | 
 | ||||||
| # Enable nightly-only features | # Enable nightly-only features | ||||||
| nightly = ["embassy/nightly", "embassy-net?/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io"] | nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io"] | ||||||
| 
 | 
 | ||||||
| # Reexport stm32-metapac at `embassy_stm32::pac`. | # Reexport stm32-metapac at `embassy_stm32::pac`. | ||||||
| # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. | # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. | ||||||
|  | |||||||
| @ -6,12 +6,12 @@ version = "0.1.0" | |||||||
| 
 | 
 | ||||||
| [features] | [features] | ||||||
| default = ["nightly"] | default = ["nightly"] | ||||||
| nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net/nightly"] | nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"] | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | ||||||
| embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | ||||||
| embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } | ||||||
| embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } | ||||||
| embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } | ||||||
| embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } | embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ version = "0.1.0" | |||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] } | embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] } | ||||||
| embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["nightly", "std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] } | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] } | ||||||
| embedded-io = { version = "0.3.0", features = ["async", "std", "futures"] } | embedded-io = { version = "0.3.0", features = ["async", "std", "futures"] } | ||||||
| 
 | 
 | ||||||
| async-io = "1.6.0" | async-io = "1.6.0" | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user