aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-net/Cargo.toml4
-rw-r--r--embassy-net/src/lib.rs5
-rw-r--r--embassy-net/src/tcp.rs353
-rw-r--r--embassy-net/src/tcp/io_impl.rs67
-rw-r--r--embassy-net/src/tcp/mod.rs192
-rw-r--r--embassy-stm32/Cargo.toml2
-rw-r--r--examples/nrf/Cargo.toml4
-rw-r--r--examples/std/Cargo.toml2
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 = []
31pool-64 = [] 31pool-64 = []
32pool-128 = [] 32pool-128 = []
33 33
34nightly = ["embedded-io/async"]
35
36[dependencies] 34[dependencies]
37 35
38defmt = { version = "0.3", optional = true } 36defmt = { version = "0.3", optional = true }
39log = { version = "0.4.14", optional = true } 37log = { version = "0.4.14", optional = true }
40 38
41embassy = { version = "0.1.0", path = "../embassy" } 39embassy = { version = "0.1.0", path = "../embassy" }
42embedded-io = "0.3.0" 40embedded-io = { version = "0.3.0", features = [ "async" ] }
43 41
44managed = { version = "0.8.0", default-features = false, features = [ "map" ] } 42managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
45heapless = { version = "0.7.5", default-features = false } 43heapless = { 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.
9pub(crate) mod fmt; 6pub(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 @@
1use core::future::Future;
2use core::marker::PhantomData;
3use core::mem;
4use core::task::Poll;
5use futures::future::poll_fn;
6use smoltcp::iface::{Context as SmolContext, SocketHandle};
7use smoltcp::socket::TcpSocket as SyncTcpSocket;
8use smoltcp::socket::{TcpSocketBuffer, TcpState};
9use smoltcp::time::Duration;
10use smoltcp::wire::IpEndpoint;
11
12use super::stack::Stack;
13
14#[derive(PartialEq, Eq, Clone, Copy, Debug)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16pub enum Error {
17 ConnectionReset,
18}
19
20#[derive(PartialEq, Eq, Clone, Copy, Debug)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub 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))]
35pub 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
44pub struct TcpSocket<'a> {
45 handle: SocketHandle,
46 ghost: PhantomData<&'a mut [u8]>,
47}
48
49impl<'a> Unpin for TcpSocket<'a> {}
50
51pub struct TcpReader<'a> {
52 handle: SocketHandle,
53 ghost: PhantomData<&'a mut [u8]>,
54}
55
56impl<'a> Unpin for TcpReader<'a> {}
57
58pub struct TcpWriter<'a> {
59 handle: SocketHandle,
60 ghost: PhantomData<&'a mut [u8]>,
61}
62
63impl<'a> Unpin for TcpWriter<'a> {}
64
65impl<'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
191fn 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
205impl<'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
213impl embedded_io::Error for Error {
214 fn kind(&self) -> embedded_io::ErrorKind {
215 embedded_io::ErrorKind::Other
216 }
217}
218
219impl<'d> embedded_io::Io for TcpSocket<'d> {
220 type Error = Error;
221}
222
223impl<'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
251impl<'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
285impl<'d> embedded_io::Io for TcpReader<'d> {
286 type Error = Error;
287}
288
289impl<'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
317impl<'d> embedded_io::Io for TcpWriter<'d> {
318 type Error = Error;
319}
320
321impl<'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 @@
1use core::future::Future;
2use core::task::Poll;
3use futures::future::poll_fn;
4
5use super::{Error, TcpSocket};
6
7impl<'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
35impl<'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 @@
1use core::marker::PhantomData;
2use core::mem;
3use core::task::Poll;
4use smoltcp::iface::{Context as SmolContext, SocketHandle};
5use smoltcp::socket::TcpSocket as SyncTcpSocket;
6use smoltcp::socket::{TcpSocketBuffer, TcpState};
7use smoltcp::time::Duration;
8use smoltcp::wire::IpEndpoint;
9
10#[cfg(feature = "nightly")]
11mod io_impl;
12
13use super::stack::Stack;
14
15#[derive(PartialEq, Eq, Clone, Copy, Debug)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub enum Error {
18 ConnectionReset,
19}
20
21#[derive(PartialEq, Eq, Clone, Copy, Debug)]
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23pub 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))]
36pub 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
45pub struct TcpSocket<'a> {
46 handle: SocketHandle,
47 ghost: PhantomData<&'a mut [u8]>,
48}
49
50impl<'a> Unpin for TcpSocket<'a> {}
51
52impl<'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
176impl<'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
184impl embedded_io::Error for Error {
185 fn kind(&self) -> embedded_io::ErrorKind {
186 embedded_io::ErrorKind::Other
187 }
188}
189
190impl<'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"]
90time-driver-tim15 = ["_time-driver"] 90time-driver-tim15 = ["_time-driver"]
91 91
92# Enable nightly-only features 92# Enable nightly-only features
93nightly = ["embassy/nightly", "embassy-net?/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io"] 93nightly = ["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]
8default = ["nightly"] 8default = ["nightly"]
9nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net/nightly"] 9nightly = ["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]
12embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 13embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
14embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } 14embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true }
15embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } 15embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
16embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } 16embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true }
17embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } 17embassy-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]
8embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] } 8embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] }
9embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["nightly", "std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] } 9embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] }
10embedded-io = { version = "0.3.0", features = ["async", "std", "futures"] } 10embedded-io = { version = "0.3.0", features = ["async", "std", "futures"] }
11 11
12async-io = "1.6.0" 12async-io = "1.6.0"