diff options
| -rw-r--r-- | embassy-net/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-net/src/lib.rs | 5 | ||||
| -rw-r--r-- | embassy-net/src/tcp.rs | 353 | ||||
| -rw-r--r-- | embassy-net/src/tcp/io_impl.rs | 67 | ||||
| -rw-r--r-- | embassy-net/src/tcp/mod.rs | 192 | ||||
| -rw-r--r-- | embassy-stm32/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/nrf/Cargo.toml | 4 | ||||
| -rw-r--r-- | examples/std/Cargo.toml | 2 |
8 files changed, 359 insertions, 270 deletions
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 8484aebc0..b58b52f18 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml | |||
| @@ -31,15 +31,13 @@ pool-32 = [] | |||
| 31 | pool-64 = [] | 31 | pool-64 = [] |
| 32 | pool-128 = [] | 32 | pool-128 = [] |
| 33 | 33 | ||
| 34 | nightly = ["embedded-io/async"] | ||
| 35 | |||
| 36 | [dependencies] | 34 | [dependencies] |
| 37 | 35 | ||
| 38 | defmt = { version = "0.3", optional = true } | 36 | defmt = { version = "0.3", optional = true } |
| 39 | log = { version = "0.4.14", optional = true } | 37 | log = { version = "0.4.14", optional = true } |
| 40 | 38 | ||
| 41 | embassy = { version = "0.1.0", path = "../embassy" } | 39 | embassy = { version = "0.1.0", path = "../embassy" } |
| 42 | embedded-io = "0.3.0" | 40 | embedded-io = { version = "0.3.0", features = [ "async" ] } |
| 43 | 41 | ||
| 44 | managed = { version = "0.8.0", default-features = false, features = [ "map" ] } | 42 | managed = { version = "0.8.0", default-features = false, features = [ "map" ] } |
| 45 | heapless = { version = "0.7.5", default-features = false } | 43 | heapless = { version = "0.7.5", default-features = false } |
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ded841909..18dc1ef61 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs | |||
| @@ -1,9 +1,6 @@ | |||
| 1 | #![cfg_attr(not(feature = "std"), no_std)] | 1 | #![cfg_attr(not(feature = "std"), no_std)] |
| 2 | #![allow(clippy::new_without_default)] | 2 | #![allow(clippy::new_without_default)] |
| 3 | #![cfg_attr( | 3 | #![feature(generic_associated_types, type_alias_impl_trait)] |
| 4 | feature = "nightly", | ||
| 5 | feature(generic_associated_types, type_alias_impl_trait) | ||
| 6 | )] | ||
| 7 | 4 | ||
| 8 | // This mod MUST go first, so that the others see its macros. | 5 | // This mod MUST go first, so that the others see its macros. |
| 9 | pub(crate) mod fmt; | 6 | pub(crate) mod fmt; |
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs new file mode 100644 index 000000000..c18651b93 --- /dev/null +++ b/embassy-net/src/tcp.rs | |||
| @@ -0,0 +1,353 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::mem; | ||
| 4 | use core::task::Poll; | ||
| 5 | use futures::future::poll_fn; | ||
| 6 | use smoltcp::iface::{Context as SmolContext, SocketHandle}; | ||
| 7 | use smoltcp::socket::TcpSocket as SyncTcpSocket; | ||
| 8 | use smoltcp::socket::{TcpSocketBuffer, TcpState}; | ||
| 9 | use smoltcp::time::Duration; | ||
| 10 | use smoltcp::wire::IpEndpoint; | ||
| 11 | |||
| 12 | use super::stack::Stack; | ||
| 13 | |||
| 14 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||
| 15 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 16 | pub enum Error { | ||
| 17 | ConnectionReset, | ||
| 18 | } | ||
| 19 | |||
| 20 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||
| 21 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 22 | pub enum ConnectError { | ||
| 23 | /// The socket is already connected or listening. | ||
| 24 | InvalidState, | ||
| 25 | /// The remote host rejected the connection with a RST packet. | ||
| 26 | ConnectionReset, | ||
| 27 | /// Connect timed out. | ||
| 28 | TimedOut, | ||
| 29 | /// No route to host. | ||
| 30 | NoRoute, | ||
| 31 | } | ||
| 32 | |||
| 33 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||
| 34 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 35 | pub enum AcceptError { | ||
| 36 | /// The socket is already connected or listening. | ||
| 37 | InvalidState, | ||
| 38 | /// Invalid listen port | ||
| 39 | InvalidPort, | ||
| 40 | /// The remote host rejected the connection with a RST packet. | ||
| 41 | ConnectionReset, | ||
| 42 | } | ||
| 43 | |||
| 44 | pub struct TcpSocket<'a> { | ||
| 45 | handle: SocketHandle, | ||
| 46 | ghost: PhantomData<&'a mut [u8]>, | ||
| 47 | } | ||
| 48 | |||
| 49 | impl<'a> Unpin for TcpSocket<'a> {} | ||
| 50 | |||
| 51 | pub struct TcpReader<'a> { | ||
| 52 | handle: SocketHandle, | ||
| 53 | ghost: PhantomData<&'a mut [u8]>, | ||
| 54 | } | ||
| 55 | |||
| 56 | impl<'a> Unpin for TcpReader<'a> {} | ||
| 57 | |||
| 58 | pub struct TcpWriter<'a> { | ||
| 59 | handle: SocketHandle, | ||
| 60 | ghost: PhantomData<&'a mut [u8]>, | ||
| 61 | } | ||
| 62 | |||
| 63 | impl<'a> Unpin for TcpWriter<'a> {} | ||
| 64 | |||
| 65 | impl<'a> TcpSocket<'a> { | ||
| 66 | pub fn new(rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { | ||
| 67 | let handle = Stack::with(|stack| { | ||
| 68 | let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; | ||
| 69 | let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; | ||
| 70 | stack.iface.add_socket(SyncTcpSocket::new( | ||
| 71 | TcpSocketBuffer::new(rx_buffer), | ||
| 72 | TcpSocketBuffer::new(tx_buffer), | ||
| 73 | )) | ||
| 74 | }); | ||
| 75 | |||
| 76 | Self { | ||
| 77 | handle, | ||
| 78 | ghost: PhantomData, | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { | ||
| 83 | ( | ||
| 84 | TcpReader { | ||
| 85 | handle: self.handle, | ||
| 86 | ghost: PhantomData, | ||
| 87 | }, | ||
| 88 | TcpWriter { | ||
| 89 | handle: self.handle, | ||
| 90 | ghost: PhantomData, | ||
| 91 | }, | ||
| 92 | ) | ||
| 93 | } | ||
| 94 | |||
| 95 | pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError> | ||
| 96 | where | ||
| 97 | T: Into<IpEndpoint>, | ||
| 98 | { | ||
| 99 | let local_port = Stack::with(|stack| stack.get_local_port()); | ||
| 100 | match with_socket(self.handle, |s, cx| { | ||
| 101 | s.connect(cx, remote_endpoint, local_port) | ||
| 102 | }) { | ||
| 103 | Ok(()) => {} | ||
| 104 | Err(smoltcp::Error::Illegal) => return Err(ConnectError::InvalidState), | ||
| 105 | Err(smoltcp::Error::Unaddressable) => return Err(ConnectError::NoRoute), | ||
| 106 | // smoltcp returns no errors other than the above. | ||
| 107 | Err(_) => unreachable!(), | ||
| 108 | } | ||
| 109 | |||
| 110 | futures::future::poll_fn(|cx| { | ||
| 111 | with_socket(self.handle, |s, _| match s.state() { | ||
| 112 | TcpState::Closed | TcpState::TimeWait => { | ||
| 113 | Poll::Ready(Err(ConnectError::ConnectionReset)) | ||
| 114 | } | ||
| 115 | TcpState::Listen => unreachable!(), | ||
| 116 | TcpState::SynSent | TcpState::SynReceived => { | ||
| 117 | s.register_send_waker(cx.waker()); | ||
| 118 | Poll::Pending | ||
| 119 | } | ||
| 120 | _ => Poll::Ready(Ok(())), | ||
| 121 | }) | ||
| 122 | }) | ||
| 123 | .await | ||
| 124 | } | ||
| 125 | |||
| 126 | pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError> | ||
| 127 | where | ||
| 128 | T: Into<IpEndpoint>, | ||
| 129 | { | ||
| 130 | match with_socket(self.handle, |s, _| s.listen(local_endpoint)) { | ||
| 131 | Ok(()) => {} | ||
| 132 | Err(smoltcp::Error::Illegal) => return Err(AcceptError::InvalidState), | ||
| 133 | Err(smoltcp::Error::Unaddressable) => return Err(AcceptError::InvalidPort), | ||
| 134 | // smoltcp returns no errors other than the above. | ||
| 135 | Err(_) => unreachable!(), | ||
| 136 | } | ||
| 137 | |||
| 138 | futures::future::poll_fn(|cx| { | ||
| 139 | with_socket(self.handle, |s, _| match s.state() { | ||
| 140 | TcpState::Listen | TcpState::SynSent | TcpState::SynReceived => { | ||
| 141 | s.register_send_waker(cx.waker()); | ||
| 142 | Poll::Pending | ||
| 143 | } | ||
| 144 | _ => Poll::Ready(Ok(())), | ||
| 145 | }) | ||
| 146 | }) | ||
| 147 | .await | ||
| 148 | } | ||
| 149 | |||
| 150 | pub fn set_timeout(&mut self, duration: Option<Duration>) { | ||
| 151 | with_socket(self.handle, |s, _| s.set_timeout(duration)) | ||
| 152 | } | ||
| 153 | |||
| 154 | pub fn set_keep_alive(&mut self, interval: Option<Duration>) { | ||
| 155 | with_socket(self.handle, |s, _| s.set_keep_alive(interval)) | ||
| 156 | } | ||
| 157 | |||
| 158 | pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) { | ||
| 159 | with_socket(self.handle, |s, _| s.set_hop_limit(hop_limit)) | ||
| 160 | } | ||
| 161 | |||
| 162 | pub fn local_endpoint(&self) -> IpEndpoint { | ||
| 163 | with_socket(self.handle, |s, _| s.local_endpoint()) | ||
| 164 | } | ||
| 165 | |||
| 166 | pub fn remote_endpoint(&self) -> IpEndpoint { | ||
| 167 | with_socket(self.handle, |s, _| s.remote_endpoint()) | ||
| 168 | } | ||
| 169 | |||
| 170 | pub fn state(&self) -> TcpState { | ||
| 171 | with_socket(self.handle, |s, _| s.state()) | ||
| 172 | } | ||
| 173 | |||
| 174 | pub fn close(&mut self) { | ||
| 175 | with_socket(self.handle, |s, _| s.close()) | ||
| 176 | } | ||
| 177 | |||
| 178 | pub fn abort(&mut self) { | ||
| 179 | with_socket(self.handle, |s, _| s.abort()) | ||
| 180 | } | ||
| 181 | |||
| 182 | pub fn may_send(&self) -> bool { | ||
| 183 | with_socket(self.handle, |s, _| s.may_send()) | ||
| 184 | } | ||
| 185 | |||
| 186 | pub fn may_recv(&self) -> bool { | ||
| 187 | with_socket(self.handle, |s, _| s.may_recv()) | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | fn with_socket<R>( | ||
| 192 | handle: SocketHandle, | ||
| 193 | f: impl FnOnce(&mut SyncTcpSocket, &mut SmolContext) -> R, | ||
| 194 | ) -> R { | ||
| 195 | Stack::with(|stack| { | ||
| 196 | let res = { | ||
| 197 | let (s, cx) = stack.iface.get_socket_and_context::<SyncTcpSocket>(handle); | ||
| 198 | f(s, cx) | ||
| 199 | }; | ||
| 200 | stack.wake(); | ||
| 201 | res | ||
| 202 | }) | ||
| 203 | } | ||
| 204 | |||
| 205 | impl<'a> Drop for TcpSocket<'a> { | ||
| 206 | fn drop(&mut self) { | ||
| 207 | Stack::with(|stack| { | ||
| 208 | stack.iface.remove_socket(self.handle); | ||
| 209 | }) | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | impl embedded_io::Error for Error { | ||
| 214 | fn kind(&self) -> embedded_io::ErrorKind { | ||
| 215 | embedded_io::ErrorKind::Other | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | impl<'d> embedded_io::Io for TcpSocket<'d> { | ||
| 220 | type Error = Error; | ||
| 221 | } | ||
| 222 | |||
| 223 | impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { | ||
| 224 | type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 225 | where | ||
| 226 | Self: 'a; | ||
| 227 | |||
| 228 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 229 | poll_fn(move |cx| { | ||
| 230 | // CAUTION: smoltcp semantics around EOF are different to what you'd expect | ||
| 231 | // from posix-like IO, so we have to tweak things here. | ||
| 232 | with_socket(self.handle, |s, _| match s.recv_slice(buf) { | ||
| 233 | // No data ready | ||
| 234 | Ok(0) => { | ||
| 235 | s.register_recv_waker(cx.waker()); | ||
| 236 | Poll::Pending | ||
| 237 | } | ||
| 238 | // Data ready! | ||
| 239 | Ok(n) => Poll::Ready(Ok(n)), | ||
| 240 | // EOF | ||
| 241 | Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)), | ||
| 242 | // Connection reset. TODO: this can also be timeouts etc, investigate. | ||
| 243 | Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||
| 244 | // smoltcp returns no errors other than the above. | ||
| 245 | Err(_) => unreachable!(), | ||
| 246 | }) | ||
| 247 | }) | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { | ||
| 252 | type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 253 | where | ||
| 254 | Self: 'a; | ||
| 255 | |||
| 256 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 257 | poll_fn(move |cx| { | ||
| 258 | with_socket(self.handle, |s, _| match s.send_slice(buf) { | ||
| 259 | // Not ready to send (no space in the tx buffer) | ||
| 260 | Ok(0) => { | ||
| 261 | s.register_send_waker(cx.waker()); | ||
| 262 | Poll::Pending | ||
| 263 | } | ||
| 264 | // Some data sent | ||
| 265 | Ok(n) => Poll::Ready(Ok(n)), | ||
| 266 | // Connection reset. TODO: this can also be timeouts etc, investigate. | ||
| 267 | Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||
| 268 | // smoltcp returns no errors other than the above. | ||
| 269 | Err(_) => unreachable!(), | ||
| 270 | }) | ||
| 271 | }) | ||
| 272 | } | ||
| 273 | |||
| 274 | type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> | ||
| 275 | where | ||
| 276 | Self: 'a; | ||
| 277 | |||
| 278 | fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { | ||
| 279 | poll_fn(move |_| { | ||
| 280 | Poll::Ready(Ok(())) // TODO: Is there a better implementation for this? | ||
| 281 | }) | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | impl<'d> embedded_io::Io for TcpReader<'d> { | ||
| 286 | type Error = Error; | ||
| 287 | } | ||
| 288 | |||
| 289 | impl<'d> embedded_io::asynch::Read for TcpReader<'d> { | ||
| 290 | type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 291 | where | ||
| 292 | Self: 'a; | ||
| 293 | |||
| 294 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 295 | poll_fn(move |cx| { | ||
| 296 | // CAUTION: smoltcp semantics around EOF are different to what you'd expect | ||
| 297 | // from posix-like IO, so we have to tweak things here. | ||
| 298 | with_socket(self.handle, |s, _| match s.recv_slice(buf) { | ||
| 299 | // No data ready | ||
| 300 | Ok(0) => { | ||
| 301 | s.register_recv_waker(cx.waker()); | ||
| 302 | Poll::Pending | ||
| 303 | } | ||
| 304 | // Data ready! | ||
| 305 | Ok(n) => Poll::Ready(Ok(n)), | ||
| 306 | // EOF | ||
| 307 | Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)), | ||
| 308 | // Connection reset. TODO: this can also be timeouts etc, investigate. | ||
| 309 | Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||
| 310 | // smoltcp returns no errors other than the above. | ||
| 311 | Err(_) => unreachable!(), | ||
| 312 | }) | ||
| 313 | }) | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | impl<'d> embedded_io::Io for TcpWriter<'d> { | ||
| 318 | type Error = Error; | ||
| 319 | } | ||
| 320 | |||
| 321 | impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { | ||
| 322 | type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 323 | where | ||
| 324 | Self: 'a; | ||
| 325 | |||
| 326 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 327 | poll_fn(move |cx| { | ||
| 328 | with_socket(self.handle, |s, _| match s.send_slice(buf) { | ||
| 329 | // Not ready to send (no space in the tx buffer) | ||
| 330 | Ok(0) => { | ||
| 331 | s.register_send_waker(cx.waker()); | ||
| 332 | Poll::Pending | ||
| 333 | } | ||
| 334 | // Some data sent | ||
| 335 | Ok(n) => Poll::Ready(Ok(n)), | ||
| 336 | // Connection reset. TODO: this can also be timeouts etc, investigate. | ||
| 337 | Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||
| 338 | // smoltcp returns no errors other than the above. | ||
| 339 | Err(_) => unreachable!(), | ||
| 340 | }) | ||
| 341 | }) | ||
| 342 | } | ||
| 343 | |||
| 344 | type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> | ||
| 345 | where | ||
| 346 | Self: 'a; | ||
| 347 | |||
| 348 | fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { | ||
| 349 | poll_fn(move |_| { | ||
| 350 | Poll::Ready(Ok(())) // TODO: Is there a better implementation for this? | ||
| 351 | }) | ||
| 352 | } | ||
| 353 | } | ||
diff --git a/embassy-net/src/tcp/io_impl.rs b/embassy-net/src/tcp/io_impl.rs deleted file mode 100644 index 155733497..000000000 --- a/embassy-net/src/tcp/io_impl.rs +++ /dev/null | |||
| @@ -1,67 +0,0 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::task::Poll; | ||
| 3 | use futures::future::poll_fn; | ||
| 4 | |||
| 5 | use super::{Error, TcpSocket}; | ||
| 6 | |||
| 7 | impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { | ||
| 8 | type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 9 | where | ||
| 10 | Self: 'a; | ||
| 11 | |||
| 12 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 13 | poll_fn(move |cx| { | ||
| 14 | // CAUTION: smoltcp semantics around EOF are different to what you'd expect | ||
| 15 | // from posix-like IO, so we have to tweak things here. | ||
| 16 | self.with(|s, _| match s.recv_slice(buf) { | ||
| 17 | // No data ready | ||
| 18 | Ok(0) => { | ||
| 19 | s.register_recv_waker(cx.waker()); | ||
| 20 | Poll::Pending | ||
| 21 | } | ||
| 22 | // Data ready! | ||
| 23 | Ok(n) => Poll::Ready(Ok(n)), | ||
| 24 | // EOF | ||
| 25 | Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)), | ||
| 26 | // Connection reset. TODO: this can also be timeouts etc, investigate. | ||
| 27 | Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||
| 28 | // smoltcp returns no errors other than the above. | ||
| 29 | Err(_) => unreachable!(), | ||
| 30 | }) | ||
| 31 | }) | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { | ||
| 36 | type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 37 | where | ||
| 38 | Self: 'a; | ||
| 39 | |||
| 40 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 41 | poll_fn(move |cx| { | ||
| 42 | self.with(|s, _| match s.send_slice(buf) { | ||
| 43 | // Not ready to send (no space in the tx buffer) | ||
| 44 | Ok(0) => { | ||
| 45 | s.register_send_waker(cx.waker()); | ||
| 46 | Poll::Pending | ||
| 47 | } | ||
| 48 | // Some data sent | ||
| 49 | Ok(n) => Poll::Ready(Ok(n)), | ||
| 50 | // Connection reset. TODO: this can also be timeouts etc, investigate. | ||
| 51 | Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||
| 52 | // smoltcp returns no errors other than the above. | ||
| 53 | Err(_) => unreachable!(), | ||
| 54 | }) | ||
| 55 | }) | ||
| 56 | } | ||
| 57 | |||
| 58 | type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> | ||
| 59 | where | ||
| 60 | Self: 'a; | ||
| 61 | |||
| 62 | fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { | ||
| 63 | poll_fn(move |_| { | ||
| 64 | Poll::Ready(Ok(())) // TODO: Is there a better implementation for this? | ||
| 65 | }) | ||
| 66 | } | ||
| 67 | } | ||
diff --git a/embassy-net/src/tcp/mod.rs b/embassy-net/src/tcp/mod.rs deleted file mode 100644 index 3bfd4c7b6..000000000 --- a/embassy-net/src/tcp/mod.rs +++ /dev/null | |||
| @@ -1,192 +0,0 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::mem; | ||
| 3 | use core::task::Poll; | ||
| 4 | use smoltcp::iface::{Context as SmolContext, SocketHandle}; | ||
| 5 | use smoltcp::socket::TcpSocket as SyncTcpSocket; | ||
| 6 | use smoltcp::socket::{TcpSocketBuffer, TcpState}; | ||
| 7 | use smoltcp::time::Duration; | ||
| 8 | use smoltcp::wire::IpEndpoint; | ||
| 9 | |||
| 10 | #[cfg(feature = "nightly")] | ||
| 11 | mod io_impl; | ||
| 12 | |||
| 13 | use super::stack::Stack; | ||
| 14 | |||
| 15 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||
| 16 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 17 | pub enum Error { | ||
| 18 | ConnectionReset, | ||
| 19 | } | ||
| 20 | |||
| 21 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||
| 22 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 23 | pub enum ConnectError { | ||
| 24 | /// The socket is already connected or listening. | ||
| 25 | InvalidState, | ||
| 26 | /// The remote host rejected the connection with a RST packet. | ||
| 27 | ConnectionReset, | ||
| 28 | /// Connect timed out. | ||
| 29 | TimedOut, | ||
| 30 | /// No route to host. | ||
| 31 | NoRoute, | ||
| 32 | } | ||
| 33 | |||
| 34 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||
| 35 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 36 | pub enum AcceptError { | ||
| 37 | /// The socket is already connected or listening. | ||
| 38 | InvalidState, | ||
| 39 | /// Invalid listen port | ||
| 40 | InvalidPort, | ||
| 41 | /// The remote host rejected the connection with a RST packet. | ||
| 42 | ConnectionReset, | ||
| 43 | } | ||
| 44 | |||
| 45 | pub struct TcpSocket<'a> { | ||
| 46 | handle: SocketHandle, | ||
| 47 | ghost: PhantomData<&'a mut [u8]>, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl<'a> Unpin for TcpSocket<'a> {} | ||
| 51 | |||
| 52 | impl<'a> TcpSocket<'a> { | ||
| 53 | pub fn new(rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { | ||
| 54 | let handle = Stack::with(|stack| { | ||
| 55 | let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; | ||
| 56 | let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; | ||
| 57 | stack.iface.add_socket(SyncTcpSocket::new( | ||
| 58 | TcpSocketBuffer::new(rx_buffer), | ||
| 59 | TcpSocketBuffer::new(tx_buffer), | ||
| 60 | )) | ||
| 61 | }); | ||
| 62 | |||
| 63 | Self { | ||
| 64 | handle, | ||
| 65 | ghost: PhantomData, | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError> | ||
| 70 | where | ||
| 71 | T: Into<IpEndpoint>, | ||
| 72 | { | ||
| 73 | let local_port = Stack::with(|stack| stack.get_local_port()); | ||
| 74 | match self.with(|s, cx| s.connect(cx, remote_endpoint, local_port)) { | ||
| 75 | Ok(()) => {} | ||
| 76 | Err(smoltcp::Error::Illegal) => return Err(ConnectError::InvalidState), | ||
| 77 | Err(smoltcp::Error::Unaddressable) => return Err(ConnectError::NoRoute), | ||
| 78 | // smoltcp returns no errors other than the above. | ||
| 79 | Err(_) => unreachable!(), | ||
| 80 | } | ||
| 81 | |||
| 82 | futures::future::poll_fn(|cx| { | ||
| 83 | self.with(|s, _| match s.state() { | ||
| 84 | TcpState::Closed | TcpState::TimeWait => { | ||
| 85 | Poll::Ready(Err(ConnectError::ConnectionReset)) | ||
| 86 | } | ||
| 87 | TcpState::Listen => unreachable!(), | ||
| 88 | TcpState::SynSent | TcpState::SynReceived => { | ||
| 89 | s.register_send_waker(cx.waker()); | ||
| 90 | Poll::Pending | ||
| 91 | } | ||
| 92 | _ => Poll::Ready(Ok(())), | ||
| 93 | }) | ||
| 94 | }) | ||
| 95 | .await | ||
| 96 | } | ||
| 97 | |||
| 98 | pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError> | ||
| 99 | where | ||
| 100 | T: Into<IpEndpoint>, | ||
| 101 | { | ||
| 102 | match self.with(|s, _| s.listen(local_endpoint)) { | ||
| 103 | Ok(()) => {} | ||
| 104 | Err(smoltcp::Error::Illegal) => return Err(AcceptError::InvalidState), | ||
| 105 | Err(smoltcp::Error::Unaddressable) => return Err(AcceptError::InvalidPort), | ||
| 106 | // smoltcp returns no errors other than the above. | ||
| 107 | Err(_) => unreachable!(), | ||
| 108 | } | ||
| 109 | |||
| 110 | futures::future::poll_fn(|cx| { | ||
| 111 | self.with(|s, _| match s.state() { | ||
| 112 | TcpState::Listen | TcpState::SynSent | TcpState::SynReceived => { | ||
| 113 | s.register_send_waker(cx.waker()); | ||
| 114 | Poll::Pending | ||
| 115 | } | ||
| 116 | _ => Poll::Ready(Ok(())), | ||
| 117 | }) | ||
| 118 | }) | ||
| 119 | .await | ||
| 120 | } | ||
| 121 | |||
| 122 | pub fn set_timeout(&mut self, duration: Option<Duration>) { | ||
| 123 | self.with(|s, _| s.set_timeout(duration)) | ||
| 124 | } | ||
| 125 | |||
| 126 | pub fn set_keep_alive(&mut self, interval: Option<Duration>) { | ||
| 127 | self.with(|s, _| s.set_keep_alive(interval)) | ||
| 128 | } | ||
| 129 | |||
| 130 | pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) { | ||
| 131 | self.with(|s, _| s.set_hop_limit(hop_limit)) | ||
| 132 | } | ||
| 133 | |||
| 134 | pub fn local_endpoint(&self) -> IpEndpoint { | ||
| 135 | self.with(|s, _| s.local_endpoint()) | ||
| 136 | } | ||
| 137 | |||
| 138 | pub fn remote_endpoint(&self) -> IpEndpoint { | ||
| 139 | self.with(|s, _| s.remote_endpoint()) | ||
| 140 | } | ||
| 141 | |||
| 142 | pub fn state(&self) -> TcpState { | ||
| 143 | self.with(|s, _| s.state()) | ||
| 144 | } | ||
| 145 | |||
| 146 | pub fn close(&mut self) { | ||
| 147 | self.with(|s, _| s.close()) | ||
| 148 | } | ||
| 149 | |||
| 150 | pub fn abort(&mut self) { | ||
| 151 | self.with(|s, _| s.abort()) | ||
| 152 | } | ||
| 153 | |||
| 154 | pub fn may_send(&self) -> bool { | ||
| 155 | self.with(|s, _| s.may_send()) | ||
| 156 | } | ||
| 157 | |||
| 158 | pub fn may_recv(&self) -> bool { | ||
| 159 | self.with(|s, _| s.may_recv()) | ||
| 160 | } | ||
| 161 | |||
| 162 | fn with<R>(&self, f: impl FnOnce(&mut SyncTcpSocket, &mut SmolContext) -> R) -> R { | ||
| 163 | Stack::with(|stack| { | ||
| 164 | let res = { | ||
| 165 | let (s, cx) = stack | ||
| 166 | .iface | ||
| 167 | .get_socket_and_context::<SyncTcpSocket>(self.handle); | ||
| 168 | f(s, cx) | ||
| 169 | }; | ||
| 170 | stack.wake(); | ||
| 171 | res | ||
| 172 | }) | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | impl<'a> Drop for TcpSocket<'a> { | ||
| 177 | fn drop(&mut self) { | ||
| 178 | Stack::with(|stack| { | ||
| 179 | stack.iface.remove_socket(self.handle); | ||
| 180 | }) | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | impl embedded_io::Error for Error { | ||
| 185 | fn kind(&self) -> embedded_io::ErrorKind { | ||
| 186 | embedded_io::ErrorKind::Other | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | impl<'d> embedded_io::Io for TcpSocket<'d> { | ||
| 191 | type Error = Error; | ||
| 192 | } | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ce36c7da2..e310d25f2 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -90,7 +90,7 @@ time-driver-tim12 = ["_time-driver"] | |||
| 90 | time-driver-tim15 = ["_time-driver"] | 90 | time-driver-tim15 = ["_time-driver"] |
| 91 | 91 | ||
| 92 | # Enable nightly-only features | 92 | # Enable nightly-only features |
| 93 | nightly = ["embassy/nightly", "embassy-net?/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io"] | 93 | nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io"] |
| 94 | 94 | ||
| 95 | # Reexport stm32-metapac at `embassy_stm32::pac`. | 95 | # Reexport stm32-metapac at `embassy_stm32::pac`. |
| 96 | # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. | 96 | # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. |
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index d96eedf98..124725f95 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml | |||
| @@ -6,12 +6,12 @@ version = "0.1.0" | |||
| 6 | 6 | ||
| 7 | [features] | 7 | [features] |
| 8 | default = ["nightly"] | 8 | default = ["nightly"] |
| 9 | 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"] | 9 | nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"] |
| 10 | 10 | ||
| 11 | [dependencies] | 11 | [dependencies] |
| 12 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 13 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 14 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | 14 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } |
| 15 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } | 15 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } |
| 16 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } | 16 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } |
| 17 | embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } | 17 | embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } |
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 863760a45..7e1c2e4bb 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml | |||
| @@ -6,7 +6,7 @@ version = "0.1.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] } | 8 | embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] } |
| 9 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["nightly", "std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] } | 9 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] } |
| 10 | embedded-io = { version = "0.3.0", features = ["async", "std", "futures"] } | 10 | embedded-io = { version = "0.3.0", features = ["async", "std", "futures"] } |
| 11 | 11 | ||
| 12 | async-io = "1.6.0" | 12 | async-io = "1.6.0" |
