From cb5931d583d283dda3a1b5ed2014c086bb8f98ae Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 3 Feb 2021 05:09:37 +0100 Subject: [PATCH 01/18] :rainbow: --- .gitignore | 3 + Cargo.toml | 48 ++++++ LICENSE-APACHE | 201 +++++++++++++++++++++++ LICENSE-MIT | 25 +++ README.md | 34 ++++ embassy-net-examples/Cargo.toml | 17 ++ embassy-net-examples/src/main.rs | 79 ++++++++++ embassy-net-examples/src/tuntap.rs | 200 +++++++++++++++++++++++ embassy-net/Cargo.toml | 46 ++++++ embassy-net/src/config/dhcp.rs | 80 ++++++++++ embassy-net/src/config/mod.rs | 34 ++++ embassy-net/src/config/statik.rs | 26 +++ embassy-net/src/device.rs | 103 ++++++++++++ embassy-net/src/fmt.rs | 118 ++++++++++++++ embassy-net/src/lib.rs | 31 ++++ embassy-net/src/packet_pool.rs | 88 +++++++++++ embassy-net/src/pool.rs | 245 +++++++++++++++++++++++++++++ embassy-net/src/stack.rs | 212 +++++++++++++++++++++++++ embassy-net/src/tcp_socket.rs | 178 +++++++++++++++++++++ test-build.sh | 25 +++ 20 files changed, 1793 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 embassy-net-examples/Cargo.toml create mode 100644 embassy-net-examples/src/main.rs create mode 100644 embassy-net-examples/src/tuntap.rs create mode 100644 embassy-net/Cargo.toml create mode 100644 embassy-net/src/config/dhcp.rs create mode 100644 embassy-net/src/config/mod.rs create mode 100644 embassy-net/src/config/statik.rs create mode 100644 embassy-net/src/device.rs create mode 100644 embassy-net/src/fmt.rs create mode 100644 embassy-net/src/lib.rs create mode 100644 embassy-net/src/packet_pool.rs create mode 100644 embassy-net/src/pool.rs create mode 100644 embassy-net/src/stack.rs create mode 100644 embassy-net/src/tcp_socket.rs create mode 100755 test-build.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..25d18d5aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +third_party diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..1b3012829 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,48 @@ + +[workspace] +members = [ + "embassy-net", + "embassy-net-examples", +] + +exclude = [ + "third_party" +] + +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true +incremental = false +opt-level = 3 +overflow-checks = true + +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false +incremental = false +lto = "fat" +opt-level = 's' +overflow-checks = false + +# do not optimize proc-macro crates = faster builds from scratch +[profile.dev.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false + +[profile.release.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false + +[patch.crates-io] +embassy = { git = "https://github.com/akiles/embassy" } +embassy-std = { git = "https://github.com/akiles/embassy" } +embassy-macros = { git = "https://github.com/akiles/embassy" } +smoltcp = { git = "https://github.com/akiles/smoltcp" } \ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000..dacc57b2b --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2020 Dario Nieuwenhuis + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 000000000..64f656709 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# embassy-net + +embassy-net contains an async network API based on smoltcp and embassy, designed +for embedded systems. + +## Running the example + +First, create the tap0 interface. You only need to do this once. + +```sh +sudo ip tuntap add name tap0 mode tap user $USER +sudo ip link set tap0 up +sudo ip addr add 192.168.69.100/24 dev tap0 +sudo ip -6 addr add fe80::100/64 dev tap0 +sudo ip -6 addr add fdaa::100/64 dev tap0 +sudo ip -6 route add fe80::/64 dev tap0 +sudo ip -6 route add fdaa::/64 dev tap0 +``` + +Then, run it + +```sh +cargo run --bin embassy-net-examples +``` + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/embassy-net-examples/Cargo.toml b/embassy-net-examples/Cargo.toml new file mode 100644 index 000000000..944e91914 --- /dev/null +++ b/embassy-net-examples/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "embassy-net-examples" +version = "0.1.0" +authors = ["Dario Nieuwenhuis "] +edition = "2018" + +[dependencies] +heapless = { version = "0.5.6", default-features = false } +embassy = { version = "0.1.0", features=["std", "log"] } +embassy-std = { version = "0.1.0" } +embassy-net = { version = "0.1.0", path = "../embassy-net", features=["std", "log"] } +env_logger = "0.8.2" +log = "0.4.11" +futures = "0.3.8" +libc = "0.2.81" +async-io = "1.3.1" +smoltcp = { version = "0.6.0", default-features = false } diff --git a/embassy-net-examples/src/main.rs b/embassy-net-examples/src/main.rs new file mode 100644 index 000000000..bc413f1a2 --- /dev/null +++ b/embassy-net-examples/src/main.rs @@ -0,0 +1,79 @@ +#![feature(type_alias_impl_trait)] + +use embassy::executor::{Spawner, task}; +use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; +use embassy::time::{Duration, Timer}; +use embassy::util::Forever; +use embassy_net::*; +use embassy_std::Executor; +use heapless::Vec; +use log::*; + +mod tuntap; + +use crate::tuntap::TunTapDevice; + +static DEVICE: Forever = Forever::new(); +static CONFIG: Forever = Forever::new(); + +#[task] +async fn net_task() { + embassy_net::run().await +} + +#[task] +async fn main_task(spawner: Spawner) { + // Init network device + let device = TunTapDevice::new("tap0").unwrap(); + + // Static IP configuration + let config = StaticConfigurator::new(UpConfig { + address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), + dns_servers: Vec::new(), + gateway: Ipv4Address::new(192, 168, 69, 100), + }); + + // Init network stack + embassy_net::init(DEVICE.put(device), CONFIG.put(config)); + + // Launch network task + spawner.spawn(net_task()).unwrap(); + + // Then we can use it! + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer); + + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + let remote_endpoint = (Ipv4Address::new(192, 168, 69, 100), 8000); + info!("connecting to {:?}...", remote_endpoint); + let r = socket.connect(remote_endpoint).await; + if let Err(e) = r { + warn!("connect error: {:?}", e); + return; + } + info!("connected!"); + loop { + let r = socket.write_all(b"Hello!\n").await; + if let Err(e) = r { + warn!("write error: {:?}", e); + return; + } + } +} + +static EXECUTOR: Forever = Forever::new(); + +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Debug) + .filter_module("async_io", log::LevelFilter::Info) + .format_timestamp_nanos() + .init(); + + let executor = EXECUTOR.put(Executor::new()); + executor.run(|spawner| { + spawner.spawn(main_task(spawner)).unwrap(); + }); +} diff --git a/embassy-net-examples/src/tuntap.rs b/embassy-net-examples/src/tuntap.rs new file mode 100644 index 000000000..5c138c069 --- /dev/null +++ b/embassy-net-examples/src/tuntap.rs @@ -0,0 +1,200 @@ +use async_io::Async; +use embassy::util::WakerRegistration; +use libc; +use smoltcp::wire::EthernetFrame; +use std::io; +use std::io::{Read, Write}; +use std::os::unix::io::{AsRawFd, RawFd}; +use log::*; + +pub const SIOCGIFMTU: libc::c_ulong = 0x8921; +pub const SIOCGIFINDEX: libc::c_ulong = 0x8933; +pub const ETH_P_ALL: libc::c_short = 0x0003; +pub const TUNSETIFF: libc::c_ulong = 0x400454CA; +pub const IFF_TUN: libc::c_int = 0x0001; +pub const IFF_TAP: libc::c_int = 0x0002; +pub const IFF_NO_PI: libc::c_int = 0x1000; + +#[repr(C)] +#[derive(Debug)] +struct ifreq { + ifr_name: [libc::c_char; libc::IF_NAMESIZE], + ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */ +} + +fn ifreq_for(name: &str) -> ifreq { + let mut ifreq = ifreq { + ifr_name: [0; libc::IF_NAMESIZE], + ifr_data: 0, + }; + for (i, byte) in name.as_bytes().iter().enumerate() { + ifreq.ifr_name[i] = *byte as libc::c_char + } + ifreq +} + +fn ifreq_ioctl( + lower: libc::c_int, + ifreq: &mut ifreq, + cmd: libc::c_ulong, +) -> io::Result { + unsafe { + let res = libc::ioctl(lower, cmd as _, ifreq as *mut ifreq); + if res == -1 { + return Err(io::Error::last_os_error()); + } + } + + Ok(ifreq.ifr_data) +} + +#[derive(Debug)] +pub struct TunTap { + fd: libc::c_int, + ifreq: ifreq, + mtu: usize, +} + +impl AsRawFd for TunTap { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +impl TunTap { + pub fn new(name: &str) -> io::Result { + unsafe { + let fd = libc::open( + "/dev/net/tun\0".as_ptr() as *const libc::c_char, + libc::O_RDWR | libc::O_NONBLOCK, + ); + if fd == -1 { + return Err(io::Error::last_os_error()); + } + + let mut ifreq = ifreq_for(name); + ifreq.ifr_data = IFF_TAP | IFF_NO_PI; + ifreq_ioctl(fd, &mut ifreq, TUNSETIFF)?; + + let socket = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP); + if socket == -1 { + return Err(io::Error::last_os_error()); + } + + let ip_mtu = ifreq_ioctl(socket, &mut ifreq, SIOCGIFMTU); + libc::close(socket); + let ip_mtu = ip_mtu? as usize; + + // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) + // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. + let mtu = ip_mtu + EthernetFrame::<&[u8]>::header_len(); + + Ok(TunTap { fd, mtu, ifreq }) + } + } +} + +impl Drop for TunTap { + fn drop(&mut self) { + unsafe { + libc::close(self.fd); + } + } +} + +impl io::Read for TunTap { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len()) }; + if len == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(len as usize) + } + } +} + +impl io::Write for TunTap { + fn write(&mut self, buf: &[u8]) -> io::Result { + let len = unsafe { libc::write(self.fd, buf.as_ptr() as *mut libc::c_void, buf.len()) }; + if len == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(len as usize) + } + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub struct TunTapDevice { + device: Async, + waker: WakerRegistration, +} + +impl TunTapDevice { + pub fn new(name: &str) -> io::Result { + Ok(Self { + device: Async::new(TunTap::new(name)?)?, + waker: WakerRegistration::new(), + }) + } +} + +use embassy_net::{LinkState, DeviceCapabilities, Packet, PacketBox, PacketBuf}; +use core::task::Waker; + +impl crate::Device for TunTapDevice { + fn is_transmit_ready(&mut self) -> bool { + true + } + + fn transmit(&mut self, pkt: PacketBuf) { + // todo handle WouldBlock + match self.device.get_mut().write(&pkt) { + Ok(_) => {} + Err(e) if e.kind() == io::ErrorKind::WouldBlock => { + info!("transmit WouldBlock"); + } + Err(e) => panic!("transmit error: {:?}", e), + } + } + + fn receive(&mut self) -> Option { + let mut pkt = PacketBox::new(Packet::new()).unwrap(); + loop { + match self.device.get_mut().read(&mut pkt[..]) { + Ok(n) => { + return Some(pkt.slice(0..n)); + } + Err(e) if e.kind() == io::ErrorKind::WouldBlock => { + let ready = if let Some(mut cx) = self.waker.context() { + let ready = self.device.poll_readable(&mut cx).is_ready(); + ready + } else { + false + }; + if !ready { + return None; + } + } + Err(e) => panic!("read error: {:?}", e), + } + } + } + + fn register_waker(&mut self, waker: &Waker) { + self.waker.register(waker) + } + + fn capabilities(&mut self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = self.device.get_ref().mtu; + caps + } + + fn link_state(&mut self) -> LinkState { + LinkState::Up + } +} diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml new file mode 100644 index 000000000..aec6b7965 --- /dev/null +++ b/embassy-net/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "embassy-net" +version = "0.1.0" +authors = ["Dario Nieuwenhuis "] +edition = "2018" + +[features] +std = [] +defmt-trace = [] +defmt-debug = [] +defmt-info = [] +defmt-warn = [] +defmt-error = [] + +[dependencies] + +defmt = { version = "0.1.3", optional = true } +log = { version = "0.4.11", optional = true } + +embassy = { version = "0.1.0" } + +managed = { version = "0.8.0", default-features = false, features = [ "map" ]} +heapless = { version = "0.5.6", default-features = false } +as-slice = { version = "0.1.4" } +generic-array = { version = "0.14.4", default-features = false } +stable_deref_trait = { version = "1.2.0", default-features = false } +futures = { version = "0.3.5", default-features = false, features = [ "async-await" ]} + +[dependencies.smoltcp] +version = "0.6.0" +#git = "https://github.com/akiles/smoltcp" +#rev = "00952e2c5cdf5667a1dfb6142258055f58d3851c" +default-features = false +features = [ + "medium-ethernet", + "medium-ip", + "proto-ipv4", + "proto-dhcpv4", + #"proto-igmp", + #"proto-ipv6", + #"socket-raw", + #"socket-icmp", + #"socket-udp", + "socket-tcp", + "async", +] diff --git a/embassy-net/src/config/dhcp.rs b/embassy-net/src/config/dhcp.rs new file mode 100644 index 000000000..f5d598bdf --- /dev/null +++ b/embassy-net/src/config/dhcp.rs @@ -0,0 +1,80 @@ +use embassy::util::Forever; +use heapless::consts::*; +use heapless::Vec; +use smoltcp::dhcp::Dhcpv4Client; +use smoltcp::socket::{RawPacketMetadata, RawSocketBuffer}; +use smoltcp::time::Instant; +use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; + +use super::*; +use crate::{device::LinkState, fmt::*}; +use crate::{Interface, SocketSet}; + +pub struct DhcpResources { + rx_buffer: [u8; 900], + tx_buffer: [u8; 600], + rx_meta: [RawPacketMetadata; 1], + tx_meta: [RawPacketMetadata; 1], +} + +pub struct DhcpConfigurator { + client: Option, +} + +impl DhcpConfigurator { + pub fn new() -> Self { + Self { client: None } + } +} + +static DHCP_RESOURCES: Forever = Forever::new(); + +impl Configurator for DhcpConfigurator { + fn poll( + &mut self, + iface: &mut Interface, + sockets: &mut SocketSet, + timestamp: Instant, + ) -> Option { + if self.client.is_none() { + let res = DHCP_RESOURCES.put(DhcpResources { + rx_buffer: [0; 900], + tx_buffer: [0; 600], + rx_meta: [RawPacketMetadata::EMPTY; 1], + tx_meta: [RawPacketMetadata::EMPTY; 1], + }); + let rx_buffer = RawSocketBuffer::new(&mut res.rx_meta[..], &mut res.rx_buffer[..]); + let tx_buffer = RawSocketBuffer::new(&mut res.tx_meta[..], &mut res.tx_buffer[..]); + let dhcp = Dhcpv4Client::new(sockets, rx_buffer, tx_buffer, timestamp); + info!("created dhcp"); + self.client = Some(dhcp) + } + + let client = self.client.as_mut().unwrap(); + + let link_up = iface.device_mut().device.link_state() == LinkState::Up; + if !link_up { + client.reset(timestamp); + return Some(Config::Down); + } + + let config = client.poll(iface, sockets, timestamp).unwrap_or(None)?; + + if config.address.is_none() { + return Some(Config::Down); + } + + let mut dns_servers = Vec::new(); + for s in &config.dns_servers { + if let Some(addr) = s { + dns_servers.push(addr.clone()).unwrap(); + } + } + + return Some(Config::Up(UpConfig { + address: config.address.unwrap(), + gateway: config.router.unwrap_or(Ipv4Address::UNSPECIFIED), + dns_servers, + })); + } +} diff --git a/embassy-net/src/config/mod.rs b/embassy-net/src/config/mod.rs new file mode 100644 index 000000000..596374f9e --- /dev/null +++ b/embassy-net/src/config/mod.rs @@ -0,0 +1,34 @@ +use heapless::consts::*; +use heapless::Vec; +use smoltcp::time::Instant; +use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; + +use crate::fmt::*; +use crate::{Interface, SocketSet}; + +mod dhcp; +mod statik; +pub use dhcp::DhcpConfigurator; +pub use statik::StaticConfigurator; + +#[derive(Debug, Clone)] +pub enum Config { + Down, + Up(UpConfig), +} + +#[derive(Debug, Clone)] +pub struct UpConfig { + pub address: Ipv4Cidr, + pub gateway: Ipv4Address, + pub dns_servers: Vec, +} + +pub trait Configurator { + fn poll( + &mut self, + iface: &mut Interface, + sockets: &mut SocketSet, + timestamp: Instant, + ) -> Option; +} diff --git a/embassy-net/src/config/statik.rs b/embassy-net/src/config/statik.rs new file mode 100644 index 000000000..52196f48a --- /dev/null +++ b/embassy-net/src/config/statik.rs @@ -0,0 +1,26 @@ +use smoltcp::time::Instant; + +use super::*; +use crate::fmt::*; +use crate::{Interface, SocketSet}; + +pub struct StaticConfigurator { + config: UpConfig, +} + +impl StaticConfigurator { + pub fn new(config: UpConfig) -> Self { + Self { config } + } +} + +impl Configurator for StaticConfigurator { + fn poll( + &mut self, + _iface: &mut Interface, + _sockets: &mut SocketSet, + _timestamp: Instant, + ) -> Option { + Some(Config::Up(self.config.clone())) + } +} diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs new file mode 100644 index 000000000..95a62e792 --- /dev/null +++ b/embassy-net/src/device.rs @@ -0,0 +1,103 @@ +use core::task::{Poll, Waker}; +use smoltcp::phy::Device as SmolDevice; +use smoltcp::phy::DeviceCapabilities; +use smoltcp::time::Instant as SmolInstant; +use smoltcp::Result; + +use crate::fmt::*; +use crate::{Packet, PacketBox, PacketBuf}; + +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum LinkState { + Down, + Up, +} + +pub trait Device { + fn is_transmit_ready(&mut self) -> bool; + fn transmit(&mut self, pkt: PacketBuf); + fn receive(&mut self) -> Option; + + fn register_waker(&mut self, waker: &Waker); + fn capabilities(&mut self) -> DeviceCapabilities; + fn link_state(&mut self) -> LinkState; +} + +pub struct DeviceAdapter { + pub device: &'static mut dyn Device, + caps: DeviceCapabilities, +} + +impl DeviceAdapter { + pub(crate) fn new(device: &'static mut dyn Device) -> Self { + Self { + caps: device.capabilities(), + device, + } + } +} + +impl<'a> SmolDevice<'a> for DeviceAdapter { + type RxToken = RxToken; + type TxToken = TxToken<'a>; + + fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + let rx_pkt = self.device.receive()?; + let tx_pkt = PacketBox::new(Packet::new()).unwrap(); // TODO: not sure about unwrap + let rx_token = RxToken { pkt: rx_pkt }; + let tx_token = TxToken { + device: self.device, + pkt: tx_pkt, + }; + + Some((rx_token, tx_token)) + } + + /// Construct a transmit token. + fn transmit(&'a mut self) -> Option { + if !self.device.is_transmit_ready() { + return None; + } + + let tx_pkt = PacketBox::new(Packet::new())?; + Some(TxToken { + device: self.device, + pkt: tx_pkt, + }) + } + + /// Get a description of device capabilities. + fn capabilities(&self) -> DeviceCapabilities { + self.caps.clone() + } +} + +pub struct RxToken { + pkt: PacketBuf, +} + +impl smoltcp::phy::RxToken for RxToken { + fn consume(mut self, _timestamp: SmolInstant, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> Result, + { + f(&mut self.pkt) + } +} + +pub struct TxToken<'a> { + device: &'a mut dyn Device, + pkt: PacketBox, +} + +impl<'a> smoltcp::phy::TxToken for TxToken<'a> { + fn consume(mut self, _timestamp: SmolInstant, len: usize, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> Result, + { + let mut buf = self.pkt.slice(0..len); + let r = f(&mut buf)?; + self.device.transmit(buf); + Ok(r) + } +} diff --git a/embassy-net/src/fmt.rs b/embassy-net/src/fmt.rs new file mode 100644 index 000000000..4da69766c --- /dev/null +++ b/embassy-net/src/fmt.rs @@ -0,0 +1,118 @@ +#![macro_use] + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +pub use fmt::*; + +#[cfg(feature = "defmt")] +mod fmt { + pub use defmt::{ + assert, assert_eq, assert_ne, debug, debug_assert, debug_assert_eq, debug_assert_ne, error, + info, panic, todo, trace, unreachable, unwrap, warn, + }; +} + +#[cfg(feature = "log")] +mod fmt { + pub use core::{ + assert, assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, panic, todo, + unreachable, + }; + pub use log::{debug, error, info, trace, warn}; +} + +#[cfg(not(any(feature = "defmt", feature = "log")))] +mod fmt { + #![macro_use] + + pub use core::{ + assert, assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, panic, todo, + unreachable, + }; + + #[macro_export] + macro_rules! trace { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! debug { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! info { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! warn { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! error { + ($($msg:expr),+ $(,)?) => { + () + }; + } +} + +#[cfg(not(feature = "defmt"))] +#[macro_export] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs new file mode 100644 index 000000000..a2a320adf --- /dev/null +++ b/embassy-net/src/lib.rs @@ -0,0 +1,31 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(const_fn)] +#![feature(const_in_array_repeat_expressions)] +#![feature(const_generics)] +#![feature(const_evaluatable_checked)] +#![allow(incomplete_features)] + +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + +mod pool; // TODO extract to embassy, or to own crate + +mod config; +mod device; +mod packet_pool; +mod stack; +mod tcp_socket; + +pub use config::{Config, Configurator, DhcpConfigurator, StaticConfigurator, UpConfig}; +pub use device::{Device, LinkState}; +pub use packet_pool::{Packet, PacketBox, PacketBuf}; +pub use stack::{init, is_init, run}; +pub use tcp_socket::TcpSocket; + +// smoltcp reexports +pub use smoltcp::phy::{DeviceCapabilities, Medium}; +pub use smoltcp::time::Duration as SmolDuration; +pub use smoltcp::time::Instant as SmolInstant; +pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; +pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>; +pub type SocketSet = smoltcp::socket::SocketSet<'static>; diff --git a/embassy-net/src/packet_pool.rs b/embassy-net/src/packet_pool.rs new file mode 100644 index 000000000..246356431 --- /dev/null +++ b/embassy-net/src/packet_pool.rs @@ -0,0 +1,88 @@ +use as_slice::{AsMutSlice, AsSlice}; +use core::ops::{Deref, DerefMut, Range}; + +use super::pool::{BitPool, Box, StaticPool}; + +pub const MTU: usize = 1514; +pub const PACKET_POOL_SIZE: usize = 4; + +pool!(pub PacketPool: [Packet; PACKET_POOL_SIZE]); +pub type PacketBox = Box; + +pub struct Packet(pub [u8; MTU]); + +impl Packet { + pub const fn new() -> Self { + Self([0; MTU]) + } +} + +impl Box { + pub fn slice(self, range: Range) -> PacketBuf { + PacketBuf { + packet: self, + range, + } + } +} + +impl AsSlice for Packet { + type Element = u8; + + fn as_slice(&self) -> &[Self::Element] { + &self.deref()[..] + } +} + +impl AsMutSlice for Packet { + fn as_mut_slice(&mut self) -> &mut [Self::Element] { + &mut self.deref_mut()[..] + } +} + +impl Deref for Packet { + type Target = [u8; MTU]; + + fn deref(&self) -> &[u8; MTU] { + &self.0 + } +} + +impl DerefMut for Packet { + fn deref_mut(&mut self) -> &mut [u8; MTU] { + &mut self.0 + } +} + +pub struct PacketBuf { + packet: PacketBox, + range: Range, +} + +impl AsSlice for PacketBuf { + type Element = u8; + + fn as_slice(&self) -> &[Self::Element] { + &self.packet[self.range.clone()] + } +} + +impl AsMutSlice for PacketBuf { + fn as_mut_slice(&mut self) -> &mut [Self::Element] { + &mut self.packet[self.range.clone()] + } +} + +impl Deref for PacketBuf { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + &self.packet[self.range.clone()] + } +} + +impl DerefMut for PacketBuf { + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.packet[self.range.clone()] + } +} diff --git a/embassy-net/src/pool.rs b/embassy-net/src/pool.rs new file mode 100644 index 000000000..3ab36e4cc --- /dev/null +++ b/embassy-net/src/pool.rs @@ -0,0 +1,245 @@ +#![macro_use] + +use as_slice::{AsMutSlice, AsSlice}; +use core::cmp; +use core::fmt; +use core::hash::{Hash, Hasher}; +use core::mem::MaybeUninit; +use core::ops::{Deref, DerefMut}; +use core::sync::atomic::{AtomicU32, Ordering}; + +use crate::fmt::{assert, *}; + +struct AtomicBitset +where + [AtomicU32; (N + 31) / 32]: Sized, +{ + used: [AtomicU32; (N + 31) / 32], +} + +impl AtomicBitset +where + [AtomicU32; (N + 31) / 32]: Sized, +{ + const fn new() -> Self { + const Z: AtomicU32 = AtomicU32::new(0); + Self { + used: [Z; (N + 31) / 32], + } + } + + fn alloc(&self) -> Option { + for (i, val) in self.used.iter().enumerate() { + let res = val.fetch_update(Ordering::AcqRel, Ordering::Acquire, |val| { + let n = val.trailing_ones() as usize + i * 32; + if n >= N { + None + } else { + Some(val | (1 << n)) + } + }); + if let Ok(val) = res { + let n = val.trailing_ones() as usize + i * 32; + return Some(n); + } + } + None + } + fn free(&self, i: usize) { + assert!(i < N); + self.used[i / 32].fetch_and(!(1 << ((i % 32) as u32)), Ordering::AcqRel); + } +} + +pub trait Pool { + fn alloc(&self) -> Option<*mut T>; + unsafe fn free(&self, p: *mut T); +} + +pub struct BitPool +where + [AtomicU32; (N + 31) / 32]: Sized, +{ + used: AtomicBitset, + data: MaybeUninit<[T; N]>, +} + +impl BitPool +where + [AtomicU32; (N + 31) / 32]: Sized, +{ + pub const fn new() -> Self { + Self { + used: AtomicBitset::new(), + data: MaybeUninit::uninit(), + } + } +} + +impl Pool for BitPool +where + [AtomicU32; (N + 31) / 32]: Sized, +{ + fn alloc(&self) -> Option<*mut T> { + let n = self.used.alloc()?; + let origin = self.data.as_ptr() as *mut T; + Some(unsafe { origin.add(n) }) + } + + /// safety: p must be a pointer obtained from self.alloc that hasn't been freed yet. + unsafe fn free(&self, p: *mut T) { + let origin = self.data.as_ptr() as *mut T; + let n = p.offset_from(origin); + assert!(n >= 0); + assert!((n as usize) < N); + self.used.free(n as usize); + } +} + +pub trait StaticPool: 'static { + type Item: 'static; + type Pool: Pool; + fn get() -> &'static Self::Pool; +} + +pub struct Box { + ptr: *mut P::Item, +} + +impl Box

{ + pub fn new(item: P::Item) -> Option { + let p = match P::get().alloc() { + Some(p) => p, + None => { + warn!("alloc failed!"); + return None; + } + }; + //trace!("allocated {:u32}", p as u32); + unsafe { p.write(item) }; + Some(Self { ptr: p }) + } +} + +impl Drop for Box

{ + fn drop(&mut self) { + unsafe { + //trace!("dropping {:u32}", self.ptr as u32); + self.ptr.drop_in_place(); + P::get().free(self.ptr); + }; + } +} + +unsafe impl Send for Box

where P::Item: Send {} + +unsafe impl Sync for Box

where P::Item: Sync {} + +unsafe impl stable_deref_trait::StableDeref for Box

{} + +impl AsSlice for Box

+where + P::Item: AsSlice, +{ + type Element = ::Element; + + fn as_slice(&self) -> &[Self::Element] { + self.deref().as_slice() + } +} + +impl AsMutSlice for Box

+where + P::Item: AsMutSlice, +{ + fn as_mut_slice(&mut self) -> &mut [Self::Element] { + self.deref_mut().as_mut_slice() + } +} + +impl Deref for Box

{ + type Target = P::Item; + + fn deref(&self) -> &P::Item { + unsafe { &*self.ptr } + } +} + +impl DerefMut for Box

{ + fn deref_mut(&mut self) -> &mut P::Item { + unsafe { &mut *self.ptr } + } +} + +impl fmt::Debug for Box

+where + P::Item: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::fmt(self, f) + } +} + +impl fmt::Display for Box

+where + P::Item: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::fmt(self, f) + } +} + +impl PartialEq for Box

+where + P::Item: PartialEq, +{ + fn eq(&self, rhs: &Box

) -> bool { + ::eq(self, rhs) + } +} + +impl Eq for Box

where P::Item: Eq {} + +impl PartialOrd for Box

+where + P::Item: PartialOrd, +{ + fn partial_cmp(&self, rhs: &Box

) -> Option { + ::partial_cmp(self, rhs) + } +} + +impl Ord for Box

+where + P::Item: Ord, +{ + fn cmp(&self, rhs: &Box

) -> cmp::Ordering { + ::cmp(self, rhs) + } +} + +impl Hash for Box

+where + P::Item: Hash, +{ + fn hash(&self, state: &mut H) + where + H: Hasher, + { + ::hash(self, state) + } +} + +macro_rules! pool { + ($vis:vis $name:ident: [$ty:ty; $size:expr]) => { + $vis struct $name; + impl StaticPool for $name { + type Item = $ty; + type Pool = BitPool<$ty, $size>; + fn get() -> &'static Self::Pool { + static POOL: BitPool<$ty, $size> = BitPool::new(); + &POOL + } + } + }; +} diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs new file mode 100644 index 000000000..c353f1bb1 --- /dev/null +++ b/embassy-net/src/stack.rs @@ -0,0 +1,212 @@ +use core::future::Future; +use core::task::Context; +use core::task::Poll; +use core::{cell::RefCell, future}; +use embassy::time::{Instant, Timer}; +use embassy::util::ThreadModeMutex; +use embassy::util::{Forever, WakerRegistration}; +use futures::pin_mut; +use smoltcp::iface::{InterfaceBuilder, Neighbor, NeighborCache, Route, Routes}; +use smoltcp::phy::Device as _; +use smoltcp::phy::Medium; +use smoltcp::socket::SocketSetItem; +use smoltcp::time::Instant as SmolInstant; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; + +use crate::device::{Device, DeviceAdapter}; +use crate::fmt::*; +use crate::{ + config::{Config, Configurator}, + device::LinkState, +}; +use crate::{Interface, SocketSet}; + +const ADDRESSES_LEN: usize = 1; +const NEIGHBOR_CACHE_LEN: usize = 8; +const SOCKETS_LEN: usize = 2; +const LOCAL_PORT_MIN: u16 = 1025; +const LOCAL_PORT_MAX: u16 = 65535; + +struct StackResources { + addresses: [IpCidr; ADDRESSES_LEN], + neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR_CACHE_LEN], + sockets: [Option>; SOCKETS_LEN], + routes: [Option<(IpCidr, Route)>; 1], +} + +static STACK_RESOURCES: Forever = Forever::new(); +static STACK: ThreadModeMutex>> = ThreadModeMutex::new(RefCell::new(None)); + +pub(crate) struct Stack { + iface: Interface, + pub sockets: SocketSet, + link_up: bool, + next_local_port: u16, + configurator: &'static mut dyn Configurator, + waker: WakerRegistration, +} + +impl Stack { + pub(crate) fn with(f: impl FnOnce(&mut Stack) -> R) -> R { + let mut stack = STACK.borrow().borrow_mut(); + let stack = stack.as_mut().unwrap(); + f(stack) + } + + pub fn get_local_port(&mut self) -> u16 { + let res = self.next_local_port; + self.next_local_port = if res >= LOCAL_PORT_MAX { + LOCAL_PORT_MIN + } else { + res + 1 + }; + res + } + + pub(crate) fn wake(&mut self) { + self.waker.wake() + } + + fn poll_configurator(&mut self, timestamp: SmolInstant) { + if let Some(config) = self + .configurator + .poll(&mut self.iface, &mut self.sockets, timestamp) + { + let medium = self.iface.device().capabilities().medium; + + let (addr, gateway) = match config { + Config::Up(config) => (config.address.into(), Some(config.gateway)), + Config::Down => (IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32), None), + }; + + self.iface.update_ip_addrs(|addrs| { + let curr_addr = &mut addrs[0]; + if *curr_addr != addr { + info!("IPv4 address: {:?} -> {:?}", *curr_addr, addr); + *curr_addr = addr; + } + }); + + if medium == Medium::Ethernet { + self.iface.routes_mut().update(|r| { + let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0); + let curr_gateway = r.get(&cidr).map(|r| r.via_router); + + if curr_gateway != gateway.map(|a| a.into()) { + info!("IPv4 gateway: {:?} -> {:?}", curr_gateway, gateway); + if let Some(gateway) = gateway { + r.insert(cidr, Route::new_ipv4_gateway(gateway)).unwrap(); + } else { + r.remove(&cidr); + } + } + }); + } + } + } + + fn poll(&mut self, cx: &mut Context<'_>) { + self.iface.device_mut().device.register_waker(cx.waker()); + self.waker.register(cx.waker()); + + let timestamp = instant_to_smoltcp(Instant::now()); + if let Err(e) = self.iface.poll(&mut self.sockets, timestamp) { + // If poll() returns error, it may not be done yet, so poll again later. + cx.waker().wake_by_ref(); + return; + } + + // Update link up + let old_link_up = self.link_up; + self.link_up = self.iface.device_mut().device.link_state() == LinkState::Up; + + // Print when changed + if old_link_up != self.link_up { + if self.link_up { + info!("Link up!"); + } else { + info!("Link down!"); + } + } + + if old_link_up || self.link_up { + self.poll_configurator(timestamp) + } + + if let Some(poll_at) = self.iface.poll_at(&mut self.sockets, timestamp) { + let t = Timer::at(instant_from_smoltcp(poll_at)); + pin_mut!(t); + if t.poll(cx).is_ready() { + cx.waker().wake_by_ref(); + } + } + } +} + +/// Initialize embassy_net. +/// This function must be called from thread mode. +pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Configurator) { + let res = STACK_RESOURCES.put(StackResources { + addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32)], + neighbor_cache: [None; NEIGHBOR_CACHE_LEN], + sockets: [None; SOCKETS_LEN], + routes: [None; 1], + }); + + let ethernet_addr = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]); + + let medium = device.capabilities().medium; + + let mut b = InterfaceBuilder::new(DeviceAdapter::new(device)); + b = b.ip_addrs(&mut res.addresses[..]); + + if medium == Medium::Ethernet { + b = b.ethernet_addr(ethernet_addr); + b = b.neighbor_cache(NeighborCache::new(&mut res.neighbor_cache[..])); + b = b.routes(Routes::new(&mut res.routes[..])); + } + + let iface = b.finalize(); + + let sockets = SocketSet::new(&mut res.sockets[..]); + + let local_port = loop { + let mut res = [0u8; 2]; + embassy::rand::rand(&mut res); + let port = u16::from_le_bytes(res); + if port >= LOCAL_PORT_MIN && port <= LOCAL_PORT_MAX { + break port; + } + }; + + let stack = Stack { + iface, + sockets, + link_up: false, + configurator, + next_local_port: local_port, + waker: WakerRegistration::new(), + }; + + *STACK.borrow().borrow_mut() = Some(stack); +} + +pub fn is_init() -> bool { + STACK.borrow().borrow().is_some() +} + +pub async fn run() { + futures::future::poll_fn(|cx| { + Stack::with(|stack| stack.poll(cx)); + Poll::<()>::Pending + }) + .await +} + +fn instant_to_smoltcp(instant: Instant) -> SmolInstant { + SmolInstant::from_millis(instant.as_millis() as i64) +} + +fn instant_from_smoltcp(instant: SmolInstant) -> Instant { + Instant::from_millis(instant.total_millis() as u64) +} diff --git a/embassy-net/src/tcp_socket.rs b/embassy-net/src/tcp_socket.rs new file mode 100644 index 000000000..7f4eb014c --- /dev/null +++ b/embassy-net/src/tcp_socket.rs @@ -0,0 +1,178 @@ +use core::marker::PhantomData; +use core::mem; +use core::pin::Pin; +use core::task::{Context, Poll}; +use embassy::io; +use embassy::io::{AsyncBufRead, AsyncWrite}; +use smoltcp::socket::SocketHandle; +use smoltcp::socket::TcpSocket as SyncTcpSocket; +use smoltcp::socket::{TcpSocketBuffer, TcpState}; +use smoltcp::time::Duration; +use smoltcp::wire::IpEndpoint; +use smoltcp::{Error, Result}; + +use super::stack::Stack; +use crate::fmt::*; + +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.sockets.add(SyncTcpSocket::new( + TcpSocketBuffer::new(rx_buffer), + TcpSocketBuffer::new(tx_buffer), + )) + }); + + Self { + handle, + ghost: PhantomData, + } + } + + pub async fn connect(&mut self, remote_endpoint: T) -> Result<()> + where + T: Into, + { + let local_port = Stack::with(|stack| stack.get_local_port()); + self.with(|s| s.connect(remote_endpoint, local_port))?; + + futures::future::poll_fn(|cx| { + self.with(|s| match s.state() { + TcpState::Closed | TcpState::TimeWait => Poll::Ready(Err(Error::Unaddressable)), + TcpState::Listen => Poll::Ready(Err(Error::Illegal)), + TcpState::SynSent | TcpState::SynReceived => { + s.register_send_waker(cx.waker()); + Poll::Pending + } + _ => Poll::Ready(Ok(())), + }) + }) + .await + } + + pub fn set_timeout(&mut self, duration: Option) { + self.with(|s| s.set_timeout(duration)) + } + + pub fn set_keep_alive(&mut self, interval: Option) { + self.with(|s| s.set_keep_alive(interval)) + } + + pub fn set_hop_limit(&mut self, hop_limit: Option) { + 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(&self, f: impl FnOnce(&mut SyncTcpSocket) -> R) -> R { + Stack::with(|stack| { + let res = { + let mut s = stack.sockets.get::(self.handle); + f(&mut *s) + }; + stack.wake(); + res + }) + } +} + +fn to_ioerr(e: Error) -> io::Error { + warn!("smoltcp err: {:?}", e); + // todo + io::Error::Other +} + +impl<'a> Drop for TcpSocket<'a> { + fn drop(&mut self) { + Stack::with(|stack| { + stack.sockets.remove(self.handle); + }) + } +} + +impl<'a> AsyncBufRead for TcpSocket<'a> { + fn poll_fill_buf<'z>( + self: Pin<&'z mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + self.with(|socket| match socket.peek(1 << 30) { + // No data ready + Ok(buf) if buf.len() == 0 => { + socket.register_recv_waker(cx.waker()); + Poll::Pending + } + // Data ready! + Ok(buf) => { + // Safety: + // - User can't touch the inner TcpSocket directly at all. + // - The socket itself won't touch these bytes until consume() is called, which + // requires the user to release this borrow. + let buf: &'z [u8] = unsafe { core::mem::transmute(&*buf) }; + Poll::Ready(Ok(buf)) + } + // EOF + Err(Error::Finished) => Poll::Ready(Ok(&[][..])), + // Error + Err(e) => Poll::Ready(Err(to_ioerr(e))), + }) + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + self.with(|s| s.recv(|_| (amt, ()))).unwrap() + } +} + +impl<'a> AsyncWrite for TcpSocket<'a> { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + 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)), + // Error + Err(e) => Poll::Ready(Err(to_ioerr(e))), + }) + } +} diff --git a/test-build.sh b/test-build.sh new file mode 100755 index 000000000..f67cc5b2b --- /dev/null +++ b/test-build.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -euxo pipefail + +# embassy std +(cd embassy; cargo build --features log,std) + +# embassy embedded +(cd embassy; cargo build --target thumbv7em-none-eabi) +(cd embassy; cargo build --target thumbv7em-none-eabi --features log) +(cd embassy; cargo build --target thumbv7em-none-eabi --features defmt) + +# embassy-nrf + +(cd embassy-nrf-examples; cargo build --target thumbv7em-none-eabi --bins) + +(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52810) +#(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52811) # nrf52811-hal doesn't exist yet +(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52832) +(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52833) +(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52840) + +(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52840,log) +(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52840,defmt) + From 10cd5bddc91280009d6951e34a67b1a01c7e2166 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 3 Feb 2021 05:15:13 +0100 Subject: [PATCH 02/18] Add CI --- .cargo/config | 2 ++ .github/workflows/rust.yml | 24 ++++++++++++++++++++++++ embassy-net/Cargo.toml | 1 + embassy-net/src/tcp_socket.rs | 1 - test-build.sh | 26 +++++++------------------- 5 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 .cargo/config create mode 100644 .github/workflows/rust.yml diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 000000000..93b20ab3f --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[unstable] +namespaced-features = true \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 000000000..a0eefd3a9 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,24 @@ +name: Rust + +on: + push: + branches: [master] + pull_request: + branches: [master] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + target: thumbv7em-none-eabi + override: true + - name: Build + run: ./test-build.sh diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index aec6b7965..185992ddc 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -11,6 +11,7 @@ defmt-debug = [] defmt-info = [] defmt-warn = [] defmt-error = [] +defmt = [ "dep:defmt", "smoltcp/defmt" ] [dependencies] diff --git a/embassy-net/src/tcp_socket.rs b/embassy-net/src/tcp_socket.rs index 7f4eb014c..eab8189de 100644 --- a/embassy-net/src/tcp_socket.rs +++ b/embassy-net/src/tcp_socket.rs @@ -112,7 +112,6 @@ impl<'a> TcpSocket<'a> { } fn to_ioerr(e: Error) -> io::Error { - warn!("smoltcp err: {:?}", e); // todo io::Error::Other } diff --git a/test-build.sh b/test-build.sh index f67cc5b2b..ec35f5da3 100755 --- a/test-build.sh +++ b/test-build.sh @@ -2,24 +2,12 @@ set -euxo pipefail -# embassy std -(cd embassy; cargo build --features log,std) +# build for std +(cd embassy-net; cargo build --features log) -# embassy embedded -(cd embassy; cargo build --target thumbv7em-none-eabi) -(cd embassy; cargo build --target thumbv7em-none-eabi --features log) -(cd embassy; cargo build --target thumbv7em-none-eabi --features defmt) - -# embassy-nrf - -(cd embassy-nrf-examples; cargo build --target thumbv7em-none-eabi --bins) - -(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52810) -#(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52811) # nrf52811-hal doesn't exist yet -(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52832) -(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52833) -(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52840) - -(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52840,log) -(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52840,defmt) +# build for embedded +(cd embassy-net; cargo build --target thumbv7em-none-eabi --features log) +(cd embassy-net; cargo build --target thumbv7em-none-eabi --features defmt) +# build examples +(cd embassy-net-examples; cargo build) From 803162f11b4c8200ce1d39b5a4588a163f1b439a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 3 Feb 2021 05:17:29 +0100 Subject: [PATCH 03/18] Actually add Ci --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a0eefd3a9..f349f50ad 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rust on: push: - branches: [master] + branches: [main] pull_request: - branches: [master] + branches: [main] env: CARGO_TERM_COLOR: always From a7d1d02be022b6dd1e5170780b509057f150ca9d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 3 Feb 2021 05:25:25 +0100 Subject: [PATCH 04/18] Remove use of feature(const_in_array_repeat_expressions) --- embassy-net/src/lib.rs | 1 - embassy-net/src/stack.rs | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index a2a320adf..fe1d529d8 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] #![feature(const_fn)] -#![feature(const_in_array_repeat_expressions)] #![feature(const_generics)] #![feature(const_evaluatable_checked)] #![allow(incomplete_features)] diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index c353f1bb1..d1dcb3bc9 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -146,10 +146,11 @@ impl Stack { /// Initialize embassy_net. /// This function must be called from thread mode. pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Configurator) { + const NONE_SOCKET: Option> = None; let res = STACK_RESOURCES.put(StackResources { addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32)], neighbor_cache: [None; NEIGHBOR_CACHE_LEN], - sockets: [None; SOCKETS_LEN], + sockets: [NONE_SOCKET; SOCKETS_LEN], routes: [None; 1], }); From 7d3878466cfecde17f5e707d65503435394f7ae5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 12 Feb 2021 01:48:21 +0100 Subject: [PATCH 05/18] reexport smoltcp error/result --- embassy-net/src/device.rs | 2 +- embassy-net/src/lib.rs | 1 + embassy-net/src/tcp_socket.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 95a62e792..ea23635c7 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -2,10 +2,10 @@ use core::task::{Poll, Waker}; use smoltcp::phy::Device as SmolDevice; use smoltcp::phy::DeviceCapabilities; use smoltcp::time::Instant as SmolInstant; -use smoltcp::Result; use crate::fmt::*; use crate::{Packet, PacketBox, PacketBuf}; +use crate::Result; #[derive(PartialEq, Eq, Clone, Copy)] pub enum LinkState { diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index fe1d529d8..de2f2ea74 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -28,3 +28,4 @@ pub use smoltcp::time::Instant as SmolInstant; pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>; pub type SocketSet = smoltcp::socket::SocketSet<'static>; +pub use smoltcp::{Error, Result}; \ No newline at end of file diff --git a/embassy-net/src/tcp_socket.rs b/embassy-net/src/tcp_socket.rs index eab8189de..84f096525 100644 --- a/embassy-net/src/tcp_socket.rs +++ b/embassy-net/src/tcp_socket.rs @@ -9,10 +9,10 @@ use smoltcp::socket::TcpSocket as SyncTcpSocket; use smoltcp::socket::{TcpSocketBuffer, TcpState}; use smoltcp::time::Duration; use smoltcp::wire::IpEndpoint; -use smoltcp::{Error, Result}; use super::stack::Stack; use crate::fmt::*; +use crate::{Error, Result}; pub struct TcpSocket<'a> { handle: SocketHandle, From f100383b3c094830fee4e54956795388bd5edec1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Feb 2021 22:31:07 +0100 Subject: [PATCH 06/18] Make ethernet address configurable from the Device --- embassy-net/src/device.rs | 1 + embassy-net/src/stack.rs | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index ea23635c7..5a266f4ef 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -21,6 +21,7 @@ pub trait Device { fn register_waker(&mut self, waker: &Waker); fn capabilities(&mut self) -> DeviceCapabilities; fn link_state(&mut self) -> LinkState; + fn ethernet_address(&mut self) -> [u8;6]; } pub struct DeviceAdapter { diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index d1dcb3bc9..8f63db971 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -154,15 +154,18 @@ pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Conf routes: [None; 1], }); - let ethernet_addr = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]); - let medium = device.capabilities().medium; + let ethernet_addr = if medium == Medium::Ethernet { + device.ethernet_address() + } else { + [0, 0, 0, 0, 0, 0] + }; let mut b = InterfaceBuilder::new(DeviceAdapter::new(device)); b = b.ip_addrs(&mut res.addresses[..]); if medium == Medium::Ethernet { - b = b.ethernet_addr(ethernet_addr); + b = b.ethernet_addr(EthernetAddress(ethernet_addr)); b = b.neighbor_cache(NeighborCache::new(&mut res.neighbor_cache[..])); b = b.routes(Routes::new(&mut res.routes[..])); } From 9bee576fd241f019c363919b0c29551c6b8ee4b2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 Mar 2021 21:20:00 +0100 Subject: [PATCH 07/18] Update embassy --- .cargo/config | 2 -- .github/workflows/rust.yml | 2 +- .vscode/settings.json | 6 ++++++ Cargo.toml | 1 + test-build.sh => ci.sh | 2 +- embassy-net-examples/src/main.rs | 5 ++--- embassy-net-examples/src/tuntap.rs | 8 ++++++-- embassy-net/Cargo.toml | 3 +-- embassy-net/src/config/dhcp.rs | 6 +++--- embassy-net/src/device.rs | 8 ++++---- embassy-net/src/stack.rs | 8 ++++++-- embassy-net/src/tcp_socket.rs | 2 +- 12 files changed, 32 insertions(+), 21 deletions(-) delete mode 100644 .cargo/config create mode 100644 .vscode/settings.json rename test-build.sh => ci.sh (92%) diff --git a/.cargo/config b/.cargo/config deleted file mode 100644 index 93b20ab3f..000000000 --- a/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[unstable] -namespaced-features = true \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f349f50ad..219a5e367 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -21,4 +21,4 @@ jobs: target: thumbv7em-none-eabi override: true - name: Build - run: ./test-build.sh + run: ./ci.sh diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..b0c623307 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "editor.formatOnSave": true, + "rust-analyzer.procMacro.enable": true, + "rust-analyzer.cargo.loadOutDirsFromCheck": true, + "rust-analyzer.assist.importMergeBehavior": "last", +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 1b3012829..7ab99c2e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,4 +45,5 @@ overflow-checks = false embassy = { git = "https://github.com/akiles/embassy" } embassy-std = { git = "https://github.com/akiles/embassy" } embassy-macros = { git = "https://github.com/akiles/embassy" } +embassy-traits = { git = "https://github.com/akiles/embassy" } smoltcp = { git = "https://github.com/akiles/smoltcp" } \ No newline at end of file diff --git a/test-build.sh b/ci.sh similarity index 92% rename from test-build.sh rename to ci.sh index ec35f5da3..385379dec 100755 --- a/test-build.sh +++ b/ci.sh @@ -7,7 +7,7 @@ set -euxo pipefail # build for embedded (cd embassy-net; cargo build --target thumbv7em-none-eabi --features log) -(cd embassy-net; cargo build --target thumbv7em-none-eabi --features defmt) +(cd embassy-net; cargo build --target thumbv7em-none-eabi --features defmt,smoltcp/defmt) # build examples (cd embassy-net-examples; cargo build) diff --git a/embassy-net-examples/src/main.rs b/embassy-net-examples/src/main.rs index bc413f1a2..dba1415b7 100644 --- a/embassy-net-examples/src/main.rs +++ b/embassy-net-examples/src/main.rs @@ -1,8 +1,7 @@ #![feature(type_alias_impl_trait)] -use embassy::executor::{Spawner, task}; -use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; -use embassy::time::{Duration, Timer}; +use embassy::executor::{task, Spawner}; +use embassy::io::AsyncWriteExt; use embassy::util::Forever; use embassy_net::*; use embassy_std::Executor; diff --git a/embassy-net-examples/src/tuntap.rs b/embassy-net-examples/src/tuntap.rs index 5c138c069..b2117e81b 100644 --- a/embassy-net-examples/src/tuntap.rs +++ b/embassy-net-examples/src/tuntap.rs @@ -1,11 +1,11 @@ use async_io::Async; use embassy::util::WakerRegistration; use libc; +use log::*; use smoltcp::wire::EthernetFrame; use std::io; use std::io::{Read, Write}; use std::os::unix::io::{AsRawFd, RawFd}; -use log::*; pub const SIOCGIFMTU: libc::c_ulong = 0x8921; pub const SIOCGIFINDEX: libc::c_ulong = 0x8933; @@ -142,8 +142,8 @@ impl TunTapDevice { } } -use embassy_net::{LinkState, DeviceCapabilities, Packet, PacketBox, PacketBuf}; use core::task::Waker; +use embassy_net::{DeviceCapabilities, LinkState, Packet, PacketBox, PacketBuf}; impl crate::Device for TunTapDevice { fn is_transmit_ready(&mut self) -> bool { @@ -197,4 +197,8 @@ impl crate::Device for TunTapDevice { fn link_state(&mut self) -> LinkState { LinkState::Up } + + fn ethernet_address(&mut self) -> [u8; 6] { + [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] + } } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 185992ddc..de625018c 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -11,11 +11,10 @@ defmt-debug = [] defmt-info = [] defmt-warn = [] defmt-error = [] -defmt = [ "dep:defmt", "smoltcp/defmt" ] [dependencies] -defmt = { version = "0.1.3", optional = true } +defmt = { version = "0.2.0", optional = true } log = { version = "0.4.11", optional = true } embassy = { version = "0.1.0" } diff --git a/embassy-net/src/config/dhcp.rs b/embassy-net/src/config/dhcp.rs index f5d598bdf..0df67baf5 100644 --- a/embassy-net/src/config/dhcp.rs +++ b/embassy-net/src/config/dhcp.rs @@ -1,13 +1,13 @@ use embassy::util::Forever; -use heapless::consts::*; use heapless::Vec; use smoltcp::dhcp::Dhcpv4Client; use smoltcp::socket::{RawPacketMetadata, RawSocketBuffer}; use smoltcp::time::Instant; -use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; +use smoltcp::wire::Ipv4Address; use super::*; -use crate::{device::LinkState, fmt::*}; +use crate::device::LinkState; +use crate::fmt::*; use crate::{Interface, SocketSet}; pub struct DhcpResources { diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 5a266f4ef..32b56e5be 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -1,11 +1,11 @@ -use core::task::{Poll, Waker}; +use core::task::Waker; use smoltcp::phy::Device as SmolDevice; use smoltcp::phy::DeviceCapabilities; use smoltcp::time::Instant as SmolInstant; use crate::fmt::*; -use crate::{Packet, PacketBox, PacketBuf}; use crate::Result; +use crate::{Packet, PacketBox, PacketBuf}; #[derive(PartialEq, Eq, Clone, Copy)] pub enum LinkState { @@ -21,7 +21,7 @@ pub trait Device { fn register_waker(&mut self, waker: &Waker); fn capabilities(&mut self) -> DeviceCapabilities; fn link_state(&mut self) -> LinkState; - fn ethernet_address(&mut self) -> [u8;6]; + fn ethernet_address(&mut self) -> [u8; 6]; } pub struct DeviceAdapter { @@ -92,7 +92,7 @@ pub struct TxToken<'a> { } impl<'a> smoltcp::phy::TxToken for TxToken<'a> { - fn consume(mut self, _timestamp: SmolInstant, len: usize, f: F) -> Result + fn consume(self, _timestamp: SmolInstant, len: usize, f: F) -> Result where F: FnOnce(&mut [u8]) -> Result, { diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 8f63db971..f8a945a54 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -1,7 +1,7 @@ +use core::cell::RefCell; use core::future::Future; use core::task::Context; use core::task::Poll; -use core::{cell::RefCell, future}; use embassy::time::{Instant, Timer}; use embassy::util::ThreadModeMutex; use embassy::util::{Forever, WakerRegistration}; @@ -110,7 +110,7 @@ impl Stack { self.waker.register(cx.waker()); let timestamp = instant_to_smoltcp(Instant::now()); - if let Err(e) = self.iface.poll(&mut self.sockets, timestamp) { + if let Err(_) = self.iface.poll(&mut self.sockets, timestamp) { // If poll() returns error, it may not be done yet, so poll again later. cx.waker().wake_by_ref(); return; @@ -174,6 +174,9 @@ pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Conf let sockets = SocketSet::new(&mut res.sockets[..]); + let local_port = LOCAL_PORT_MIN; + + /* let local_port = loop { let mut res = [0u8; 2]; embassy::rand::rand(&mut res); @@ -182,6 +185,7 @@ pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Conf break port; } }; + */ let stack = Stack { iface, diff --git a/embassy-net/src/tcp_socket.rs b/embassy-net/src/tcp_socket.rs index 84f096525..4f43bc611 100644 --- a/embassy-net/src/tcp_socket.rs +++ b/embassy-net/src/tcp_socket.rs @@ -111,7 +111,7 @@ impl<'a> TcpSocket<'a> { } } -fn to_ioerr(e: Error) -> io::Error { +fn to_ioerr(_err: Error) -> io::Error { // todo io::Error::Other } From 9c5a8b945a743e75d586fdc2ef857d7c2a038e7d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Apr 2021 19:06:45 +0200 Subject: [PATCH 08/18] Update to latest embassy, atomic-pool, smoltcp --- Cargo.toml | 10 +- embassy-net-examples/Cargo.toml | 5 +- embassy-net-examples/src/main.rs | 36 +++-- embassy-net-examples/src/tuntap.rs | 35 ++++- embassy-net/Cargo.toml | 10 +- embassy-net/src/config/dhcp.rs | 73 +++------ embassy-net/src/config/mod.rs | 30 ++-- embassy-net/src/config/statik.rs | 19 ++- embassy-net/src/device.rs | 1 + embassy-net/src/lib.rs | 13 +- embassy-net/src/packet_pool.rs | 10 +- embassy-net/src/pool.rs | 245 ----------------------------- embassy-net/src/stack.rs | 73 +++++---- 13 files changed, 182 insertions(+), 378 deletions(-) delete mode 100644 embassy-net/src/pool.rs diff --git a/Cargo.toml b/Cargo.toml index 7ab99c2e9..8ed84db99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,8 +42,8 @@ opt-level = 0 overflow-checks = false [patch.crates-io] -embassy = { git = "https://github.com/akiles/embassy" } -embassy-std = { git = "https://github.com/akiles/embassy" } -embassy-macros = { git = "https://github.com/akiles/embassy" } -embassy-traits = { git = "https://github.com/akiles/embassy" } -smoltcp = { git = "https://github.com/akiles/smoltcp" } \ No newline at end of file +embassy = { git = "https://github.com/embassy-rs/embassy" } +embassy-std = { git = "https://github.com/embassy-rs/embassy" } +embassy-macros = { git = "https://github.com/embassy-rs/embassy" } +embassy-traits = { git = "https://github.com/embassy-rs/embassy" } +smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="9ce3d9505ef5455bb049713b9262561d78ebf330" } \ No newline at end of file diff --git a/embassy-net-examples/Cargo.toml b/embassy-net-examples/Cargo.toml index 944e91914..796c44745 100644 --- a/embassy-net-examples/Cargo.toml +++ b/embassy-net-examples/Cargo.toml @@ -8,10 +8,11 @@ edition = "2018" heapless = { version = "0.5.6", default-features = false } embassy = { version = "0.1.0", features=["std", "log"] } embassy-std = { version = "0.1.0" } -embassy-net = { version = "0.1.0", path = "../embassy-net", features=["std", "log"] } +embassy-net = { version = "0.1.0", path = "../embassy-net", features=["std", "log", "tcp", "dhcpv4"] } env_logger = "0.8.2" log = "0.4.11" futures = "0.3.8" libc = "0.2.81" async-io = "1.3.1" -smoltcp = { version = "0.6.0", default-features = false } +smoltcp = { version = "0.7.0", default-features = false } +clap = { version = "3.0.0-beta.2", features = ["derive"] } \ No newline at end of file diff --git a/embassy-net-examples/src/main.rs b/embassy-net-examples/src/main.rs index dba1415b7..c157ea34e 100644 --- a/embassy-net-examples/src/main.rs +++ b/embassy-net-examples/src/main.rs @@ -1,6 +1,10 @@ #![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] +#![allow(incomplete_features)] -use embassy::executor::{task, Spawner}; +use clap::{AppSettings, Clap}; +use embassy::executor::Spawner; use embassy::io::AsyncWriteExt; use embassy::util::Forever; use embassy_net::*; @@ -13,25 +17,39 @@ mod tuntap; use crate::tuntap::TunTapDevice; static DEVICE: Forever = Forever::new(); -static CONFIG: Forever = Forever::new(); +static CONFIG: Forever = Forever::new(); -#[task] +#[derive(Clap)] +#[clap(version = "1.0")] +#[clap(setting = AppSettings::ColoredHelp)] +struct Opts { + /// TAP device name + #[clap(long, default_value = "tap0")] + tap: String, +} + +#[embassy::task] async fn net_task() { embassy_net::run().await } -#[task] +#[embassy::task] async fn main_task(spawner: Spawner) { + let opts: Opts = Opts::parse(); + // Init network device - let device = TunTapDevice::new("tap0").unwrap(); + let device = TunTapDevice::new(&opts.tap).unwrap(); // Static IP configuration - let config = StaticConfigurator::new(UpConfig { - address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), + let config = StaticConfigurator::new(Config { + address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), - gateway: Ipv4Address::new(192, 168, 69, 100), + gateway: Some(Ipv4Address::new(192, 168, 69, 1)), }); + // DHCP configruation + let config = DhcpConfigurator::new(); + // Init network stack embassy_net::init(DEVICE.put(device), CONFIG.put(config)); @@ -45,7 +63,7 @@ async fn main_task(spawner: Spawner) { socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); - let remote_endpoint = (Ipv4Address::new(192, 168, 69, 100), 8000); + let remote_endpoint = (Ipv4Address::new(192, 168, 69, 74), 8000); info!("connecting to {:?}...", remote_endpoint); let r = socket.connect(remote_endpoint).await; if let Err(e) = r { diff --git a/embassy-net-examples/src/tuntap.rs b/embassy-net-examples/src/tuntap.rs index b2117e81b..dd453deb3 100644 --- a/embassy-net-examples/src/tuntap.rs +++ b/embassy-net-examples/src/tuntap.rs @@ -1,5 +1,4 @@ use async_io::Async; -use embassy::util::WakerRegistration; use libc; use log::*; use smoltcp::wire::EthernetFrame; @@ -130,20 +129,21 @@ impl io::Write for TunTap { pub struct TunTapDevice { device: Async, - waker: WakerRegistration, + waker: Option, } impl TunTapDevice { pub fn new(name: &str) -> io::Result { Ok(Self { device: Async::new(TunTap::new(name)?)?, - waker: WakerRegistration::new(), + waker: None, }) } } use core::task::Waker; -use embassy_net::{DeviceCapabilities, LinkState, Packet, PacketBox, PacketBuf}; +use embassy_net::{DeviceCapabilities, LinkState, Packet, PacketBox, PacketBoxExt, PacketBuf}; +use std::task::Context; impl crate::Device for TunTapDevice { fn is_transmit_ready(&mut self) -> bool { @@ -169,7 +169,8 @@ impl crate::Device for TunTapDevice { return Some(pkt.slice(0..n)); } Err(e) if e.kind() == io::ErrorKind::WouldBlock => { - let ready = if let Some(mut cx) = self.waker.context() { + let ready = if let Some(w) = self.waker.as_ref() { + let mut cx = Context::from_waker(w); let ready = self.device.poll_readable(&mut cx).is_ready(); ready } else { @@ -184,8 +185,28 @@ impl crate::Device for TunTapDevice { } } - fn register_waker(&mut self, waker: &Waker) { - self.waker.register(waker) + fn register_waker(&mut self, w: &Waker) { + match self.waker { + // Optimization: If both the old and new Wakers wake the same task, we can simply + // keep the old waker, skipping the clone. (In most executor implementations, + // cloning a waker is somewhat expensive, comparable to cloning an Arc). + Some(ref w2) if (w2.will_wake(w)) => {} + _ => { + // clone the new waker and store it + if let Some(old_waker) = core::mem::replace(&mut self.waker, Some(w.clone())) { + // We had a waker registered for another task. Wake it, so the other task can + // reregister itself if it's still interested. + // + // If two tasks are waiting on the same thing concurrently, this will cause them + // to wake each other in a loop fighting over this WakerRegistration. This wastes + // CPU but things will still work. + // + // If the user wants to have two tasks waiting on the same thing they should use + // a more appropriate primitive that can store multiple wakers. + old_waker.wake() + } + } + } } fn capabilities(&mut self) -> DeviceCapabilities { diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index de625018c..dccd7984d 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -12,6 +12,9 @@ defmt-info = [] defmt-warn = [] defmt-error = [] +tcp = ["smoltcp/socket-tcp"] +dhcpv4 = ["smoltcp/socket-dhcpv4"] + [dependencies] defmt = { version = "0.2.0", optional = true } @@ -25,9 +28,10 @@ as-slice = { version = "0.1.4" } generic-array = { version = "0.14.4", default-features = false } stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.5", default-features = false, features = [ "async-await" ]} +atomic-pool = "0.1.0" [dependencies.smoltcp] -version = "0.6.0" +version = "0.7.0" #git = "https://github.com/akiles/smoltcp" #rev = "00952e2c5cdf5667a1dfb6142258055f58d3851c" default-features = false @@ -35,12 +39,12 @@ features = [ "medium-ethernet", "medium-ip", "proto-ipv4", - "proto-dhcpv4", + #"proto-dhcpv4", #"proto-igmp", #"proto-ipv6", #"socket-raw", #"socket-icmp", #"socket-udp", - "socket-tcp", + #"socket-tcp", "async", ] diff --git a/embassy-net/src/config/dhcp.rs b/embassy-net/src/config/dhcp.rs index 0df67baf5..007c398b3 100644 --- a/embassy-net/src/config/dhcp.rs +++ b/embassy-net/src/config/dhcp.rs @@ -1,80 +1,59 @@ -use embassy::util::Forever; use heapless::Vec; -use smoltcp::dhcp::Dhcpv4Client; -use smoltcp::socket::{RawPacketMetadata, RawSocketBuffer}; +use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket, SocketHandle}; use smoltcp::time::Instant; -use smoltcp::wire::Ipv4Address; use super::*; use crate::device::LinkState; use crate::fmt::*; use crate::{Interface, SocketSet}; -pub struct DhcpResources { - rx_buffer: [u8; 900], - tx_buffer: [u8; 600], - rx_meta: [RawPacketMetadata; 1], - tx_meta: [RawPacketMetadata; 1], -} - pub struct DhcpConfigurator { - client: Option, + handle: Option, } impl DhcpConfigurator { pub fn new() -> Self { - Self { client: None } + Self { handle: None } } } -static DHCP_RESOURCES: Forever = Forever::new(); - impl Configurator for DhcpConfigurator { fn poll( &mut self, iface: &mut Interface, sockets: &mut SocketSet, - timestamp: Instant, - ) -> Option { - if self.client.is_none() { - let res = DHCP_RESOURCES.put(DhcpResources { - rx_buffer: [0; 900], - tx_buffer: [0; 600], - rx_meta: [RawPacketMetadata::EMPTY; 1], - tx_meta: [RawPacketMetadata::EMPTY; 1], - }); - let rx_buffer = RawSocketBuffer::new(&mut res.rx_meta[..], &mut res.rx_buffer[..]); - let tx_buffer = RawSocketBuffer::new(&mut res.tx_meta[..], &mut res.tx_buffer[..]); - let dhcp = Dhcpv4Client::new(sockets, rx_buffer, tx_buffer, timestamp); - info!("created dhcp"); - self.client = Some(dhcp) + _timestamp: Instant, + ) -> Event { + if self.handle.is_none() { + let handle = sockets.add(Dhcpv4Socket::new()); + self.handle = Some(handle) } - let client = self.client.as_mut().unwrap(); + let mut socket = sockets.get::(self.handle.unwrap()); let link_up = iface.device_mut().device.link_state() == LinkState::Up; if !link_up { - client.reset(timestamp); - return Some(Config::Down); + socket.reset(); + return Event::Deconfigured; } - let config = client.poll(iface, sockets, timestamp).unwrap_or(None)?; + match socket.poll() { + Dhcpv4Event::NoChange => Event::NoChange, + Dhcpv4Event::Deconfigured => Event::Deconfigured, + Dhcpv4Event::Configured(config) => { + let mut dns_servers = Vec::new(); + for s in &config.dns_servers { + if let Some(addr) = s { + dns_servers.push(addr.clone()).unwrap(); + } + } - if config.address.is_none() { - return Some(Config::Down); - } - - let mut dns_servers = Vec::new(); - for s in &config.dns_servers { - if let Some(addr) = s { - dns_servers.push(addr.clone()).unwrap(); + Event::Configured(Config { + address: config.address, + gateway: config.router, + dns_servers, + }) } } - - return Some(Config::Up(UpConfig { - address: config.address.unwrap(), - gateway: config.router.unwrap_or(Ipv4Address::UNSPECIFIED), - dns_servers, - })); } } diff --git a/embassy-net/src/config/mod.rs b/embassy-net/src/config/mod.rs index 596374f9e..a090aaacf 100644 --- a/embassy-net/src/config/mod.rs +++ b/embassy-net/src/config/mod.rs @@ -6,29 +6,33 @@ use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; use crate::fmt::*; use crate::{Interface, SocketSet}; -mod dhcp; mod statik; -pub use dhcp::DhcpConfigurator; pub use statik::StaticConfigurator; +#[cfg(feature = "dhcpv4")] +mod dhcp; +#[cfg(feature = "dhcpv4")] +pub use dhcp::DhcpConfigurator; + +/// Return value for the `Configurator::poll` function #[derive(Debug, Clone)] -pub enum Config { - Down, - Up(UpConfig), +pub enum Event { + /// No change has occured to the configuration. + NoChange, + /// Configuration has been lost (for example, DHCP lease has expired) + Deconfigured, + /// Configuration has been newly acquired, or modified. + Configured(Config), } #[derive(Debug, Clone)] -pub struct UpConfig { +pub struct Config { pub address: Ipv4Cidr, - pub gateway: Ipv4Address, + pub gateway: Option, pub dns_servers: Vec, } pub trait Configurator { - fn poll( - &mut self, - iface: &mut Interface, - sockets: &mut SocketSet, - timestamp: Instant, - ) -> Option; + fn poll(&mut self, iface: &mut Interface, sockets: &mut SocketSet, timestamp: Instant) + -> Event; } diff --git a/embassy-net/src/config/statik.rs b/embassy-net/src/config/statik.rs index 52196f48a..912143bff 100644 --- a/embassy-net/src/config/statik.rs +++ b/embassy-net/src/config/statik.rs @@ -5,12 +5,16 @@ use crate::fmt::*; use crate::{Interface, SocketSet}; pub struct StaticConfigurator { - config: UpConfig, + config: Config, + returned: bool, } impl StaticConfigurator { - pub fn new(config: UpConfig) -> Self { - Self { config } + pub fn new(config: Config) -> Self { + Self { + config, + returned: false, + } } } @@ -20,7 +24,12 @@ impl Configurator for StaticConfigurator { _iface: &mut Interface, _sockets: &mut SocketSet, _timestamp: Instant, - ) -> Option { - Some(Config::Up(self.config.clone())) + ) -> Event { + if self.returned { + Event::NoChange + } else { + self.returned = true; + Event::Configured(self.config.clone()) + } } } diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 32b56e5be..6c06b0605 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -4,6 +4,7 @@ use smoltcp::phy::DeviceCapabilities; use smoltcp::time::Instant as SmolInstant; use crate::fmt::*; +use crate::packet_pool::PacketBoxExt; use crate::Result; use crate::{Packet, PacketBox, PacketBuf}; diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index de2f2ea74..c79c38c0b 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -7,18 +7,19 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -mod pool; // TODO extract to embassy, or to own crate - mod config; mod device; mod packet_pool; mod stack; -mod tcp_socket; -pub use config::{Config, Configurator, DhcpConfigurator, StaticConfigurator, UpConfig}; +pub use config::{Config, Configurator, DhcpConfigurator, StaticConfigurator}; pub use device::{Device, LinkState}; -pub use packet_pool::{Packet, PacketBox, PacketBuf}; +pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf}; pub use stack::{init, is_init, run}; + +#[cfg(feature = "tcp")] +mod tcp_socket; +#[cfg(feature = "tcp")] pub use tcp_socket::TcpSocket; // smoltcp reexports @@ -28,4 +29,4 @@ pub use smoltcp::time::Instant as SmolInstant; pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>; pub type SocketSet = smoltcp::socket::SocketSet<'static>; -pub use smoltcp::{Error, Result}; \ No newline at end of file +pub use smoltcp::{Error, Result}; diff --git a/embassy-net/src/packet_pool.rs b/embassy-net/src/packet_pool.rs index 246356431..2c27d4013 100644 --- a/embassy-net/src/packet_pool.rs +++ b/embassy-net/src/packet_pool.rs @@ -1,7 +1,7 @@ use as_slice::{AsMutSlice, AsSlice}; use core::ops::{Deref, DerefMut, Range}; -use super::pool::{BitPool, Box, StaticPool}; +use atomic_pool::{pool, Box}; pub const MTU: usize = 1514; pub const PACKET_POOL_SIZE: usize = 4; @@ -17,8 +17,12 @@ impl Packet { } } -impl Box { - pub fn slice(self, range: Range) -> PacketBuf { +pub trait PacketBoxExt { + fn slice(self, range: Range) -> PacketBuf; +} + +impl PacketBoxExt for PacketBox { + fn slice(self, range: Range) -> PacketBuf { PacketBuf { packet: self, range, diff --git a/embassy-net/src/pool.rs b/embassy-net/src/pool.rs deleted file mode 100644 index 3ab36e4cc..000000000 --- a/embassy-net/src/pool.rs +++ /dev/null @@ -1,245 +0,0 @@ -#![macro_use] - -use as_slice::{AsMutSlice, AsSlice}; -use core::cmp; -use core::fmt; -use core::hash::{Hash, Hasher}; -use core::mem::MaybeUninit; -use core::ops::{Deref, DerefMut}; -use core::sync::atomic::{AtomicU32, Ordering}; - -use crate::fmt::{assert, *}; - -struct AtomicBitset -where - [AtomicU32; (N + 31) / 32]: Sized, -{ - used: [AtomicU32; (N + 31) / 32], -} - -impl AtomicBitset -where - [AtomicU32; (N + 31) / 32]: Sized, -{ - const fn new() -> Self { - const Z: AtomicU32 = AtomicU32::new(0); - Self { - used: [Z; (N + 31) / 32], - } - } - - fn alloc(&self) -> Option { - for (i, val) in self.used.iter().enumerate() { - let res = val.fetch_update(Ordering::AcqRel, Ordering::Acquire, |val| { - let n = val.trailing_ones() as usize + i * 32; - if n >= N { - None - } else { - Some(val | (1 << n)) - } - }); - if let Ok(val) = res { - let n = val.trailing_ones() as usize + i * 32; - return Some(n); - } - } - None - } - fn free(&self, i: usize) { - assert!(i < N); - self.used[i / 32].fetch_and(!(1 << ((i % 32) as u32)), Ordering::AcqRel); - } -} - -pub trait Pool { - fn alloc(&self) -> Option<*mut T>; - unsafe fn free(&self, p: *mut T); -} - -pub struct BitPool -where - [AtomicU32; (N + 31) / 32]: Sized, -{ - used: AtomicBitset, - data: MaybeUninit<[T; N]>, -} - -impl BitPool -where - [AtomicU32; (N + 31) / 32]: Sized, -{ - pub const fn new() -> Self { - Self { - used: AtomicBitset::new(), - data: MaybeUninit::uninit(), - } - } -} - -impl Pool for BitPool -where - [AtomicU32; (N + 31) / 32]: Sized, -{ - fn alloc(&self) -> Option<*mut T> { - let n = self.used.alloc()?; - let origin = self.data.as_ptr() as *mut T; - Some(unsafe { origin.add(n) }) - } - - /// safety: p must be a pointer obtained from self.alloc that hasn't been freed yet. - unsafe fn free(&self, p: *mut T) { - let origin = self.data.as_ptr() as *mut T; - let n = p.offset_from(origin); - assert!(n >= 0); - assert!((n as usize) < N); - self.used.free(n as usize); - } -} - -pub trait StaticPool: 'static { - type Item: 'static; - type Pool: Pool; - fn get() -> &'static Self::Pool; -} - -pub struct Box { - ptr: *mut P::Item, -} - -impl Box

{ - pub fn new(item: P::Item) -> Option { - let p = match P::get().alloc() { - Some(p) => p, - None => { - warn!("alloc failed!"); - return None; - } - }; - //trace!("allocated {:u32}", p as u32); - unsafe { p.write(item) }; - Some(Self { ptr: p }) - } -} - -impl Drop for Box

{ - fn drop(&mut self) { - unsafe { - //trace!("dropping {:u32}", self.ptr as u32); - self.ptr.drop_in_place(); - P::get().free(self.ptr); - }; - } -} - -unsafe impl Send for Box

where P::Item: Send {} - -unsafe impl Sync for Box

where P::Item: Sync {} - -unsafe impl stable_deref_trait::StableDeref for Box

{} - -impl AsSlice for Box

-where - P::Item: AsSlice, -{ - type Element = ::Element; - - fn as_slice(&self) -> &[Self::Element] { - self.deref().as_slice() - } -} - -impl AsMutSlice for Box

-where - P::Item: AsMutSlice, -{ - fn as_mut_slice(&mut self) -> &mut [Self::Element] { - self.deref_mut().as_mut_slice() - } -} - -impl Deref for Box

{ - type Target = P::Item; - - fn deref(&self) -> &P::Item { - unsafe { &*self.ptr } - } -} - -impl DerefMut for Box

{ - fn deref_mut(&mut self) -> &mut P::Item { - unsafe { &mut *self.ptr } - } -} - -impl fmt::Debug for Box

-where - P::Item: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ::fmt(self, f) - } -} - -impl fmt::Display for Box

-where - P::Item: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ::fmt(self, f) - } -} - -impl PartialEq for Box

-where - P::Item: PartialEq, -{ - fn eq(&self, rhs: &Box

) -> bool { - ::eq(self, rhs) - } -} - -impl Eq for Box

where P::Item: Eq {} - -impl PartialOrd for Box

-where - P::Item: PartialOrd, -{ - fn partial_cmp(&self, rhs: &Box

) -> Option { - ::partial_cmp(self, rhs) - } -} - -impl Ord for Box

-where - P::Item: Ord, -{ - fn cmp(&self, rhs: &Box

) -> cmp::Ordering { - ::cmp(self, rhs) - } -} - -impl Hash for Box

-where - P::Item: Hash, -{ - fn hash(&self, state: &mut H) - where - H: Hasher, - { - ::hash(self, state) - } -} - -macro_rules! pool { - ($vis:vis $name:ident: [$ty:ty; $size:expr]) => { - $vis struct $name; - impl StaticPool for $name { - type Item = $ty; - type Pool = BitPool<$ty, $size>; - fn get() -> &'static Self::Pool { - static POOL: BitPool<$ty, $size> = BitPool::new(); - &POOL - } - } - }; -} diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index f8a945a54..dc8043e67 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -11,14 +11,12 @@ use smoltcp::phy::Device as _; use smoltcp::phy::Medium; use smoltcp::socket::SocketSetItem; use smoltcp::time::Instant as SmolInstant; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; -use crate::device::{Device, DeviceAdapter}; +use crate::config::Configurator; +use crate::config::Event; +use crate::device::{Device, DeviceAdapter, LinkState}; use crate::fmt::*; -use crate::{ - config::{Config, Configurator}, - device::LinkState, -}; use crate::{Interface, SocketSet}; const ADDRESSES_LEN: usize = 1; @@ -68,39 +66,41 @@ impl Stack { } fn poll_configurator(&mut self, timestamp: SmolInstant) { - if let Some(config) = self + let medium = self.iface.device().capabilities().medium; + + match self .configurator .poll(&mut self.iface, &mut self.sockets, timestamp) { - let medium = self.iface.device().capabilities().medium; + Event::NoChange => {} + Event::Configured(config) => { + debug!("Acquired IP configuration:"); - let (addr, gateway) = match config { - Config::Up(config) => (config.address.into(), Some(config.gateway)), - Config::Down => (IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32), None), - }; + debug!(" IP address: {}", config.address); + set_ipv4_addr(&mut self.iface, config.address); - self.iface.update_ip_addrs(|addrs| { - let curr_addr = &mut addrs[0]; - if *curr_addr != addr { - info!("IPv4 address: {:?} -> {:?}", *curr_addr, addr); - *curr_addr = addr; - } - }); - - if medium == Medium::Ethernet { - self.iface.routes_mut().update(|r| { - let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0); - let curr_gateway = r.get(&cidr).map(|r| r.via_router); - - if curr_gateway != gateway.map(|a| a.into()) { - info!("IPv4 gateway: {:?} -> {:?}", curr_gateway, gateway); - if let Some(gateway) = gateway { - r.insert(cidr, Route::new_ipv4_gateway(gateway)).unwrap(); - } else { - r.remove(&cidr); - } + if medium == Medium::Ethernet { + if let Some(gateway) = config.gateway { + debug!(" Default gateway: {}", gateway); + self.iface + .routes_mut() + .add_default_ipv4_route(gateway) + .unwrap(); + } else { + debug!(" Default gateway: None"); + self.iface.routes_mut().remove_default_ipv4_route(); } - }); + } + for (i, s) in config.dns_servers.iter().enumerate() { + debug!(" DNS server {}: {}", i, s); + } + } + Event::Deconfigured => { + debug!("Lost IP configuration"); + set_ipv4_addr(&mut self.iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); + if medium == Medium::Ethernet { + self.iface.routes_mut().remove_default_ipv4_route(); + } } } } @@ -143,6 +143,13 @@ impl Stack { } } +fn set_ipv4_addr(iface: &mut Interface, cidr: Ipv4Cidr) { + iface.update_ip_addrs(|addrs| { + let dest = addrs.iter_mut().next().unwrap(); + *dest = IpCidr::Ipv4(cidr); + }); +} + /// Initialize embassy_net. /// This function must be called from thread mode. pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Configurator) { From 54d6b6ec48145ecc33a35674f9b546b9abb01759 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Apr 2021 15:35:54 +0200 Subject: [PATCH 09/18] Correctly randomize source port --- embassy-net/src/stack.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index dc8043e67..9b0dd54d4 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -181,18 +181,14 @@ pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Conf let sockets = SocketSet::new(&mut res.sockets[..]); - let local_port = LOCAL_PORT_MIN; - - /* let local_port = loop { let mut res = [0u8; 2]; - embassy::rand::rand(&mut res); + rand(&mut res); let port = u16::from_le_bytes(res); if port >= LOCAL_PORT_MIN && port <= LOCAL_PORT_MAX { break port; } }; - */ let stack = Stack { iface, @@ -225,3 +221,11 @@ fn instant_to_smoltcp(instant: Instant) -> SmolInstant { fn instant_from_smoltcp(instant: SmolInstant) -> Instant { Instant::from_millis(instant.total_millis() as u64) } + +extern "Rust" { + fn _embassy_rand(buf: &mut [u8]); +} + +fn rand(buf: &mut [u8]) { + unsafe { _embassy_rand(buf) } +} From ccdcd9df17748effd5f8d929b2d44ff083197993 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Apr 2021 17:24:52 +0200 Subject: [PATCH 10/18] publish config::Event --- embassy-net/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index c79c38c0b..edfb2c26d 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -12,7 +12,9 @@ mod device; mod packet_pool; mod stack; -pub use config::{Config, Configurator, DhcpConfigurator, StaticConfigurator}; +pub use config::{ + Config, Configurator, DhcpConfigurator, Event as ConfigEvent, StaticConfigurator, +}; pub use device::{Device, LinkState}; pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf}; pub use stack::{init, is_init, run}; From 439092dc5974ee06266fae14198e6a9f36732780 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Apr 2021 18:13:22 +0200 Subject: [PATCH 11/18] derive Eq for config --- embassy-net/src/config/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/config/mod.rs b/embassy-net/src/config/mod.rs index a090aaacf..16470f7e6 100644 --- a/embassy-net/src/config/mod.rs +++ b/embassy-net/src/config/mod.rs @@ -25,7 +25,7 @@ pub enum Event { Configured(Config), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Config { pub address: Ipv4Cidr, pub gateway: Option, From 28c235d7866f2a0defaf235270ade6dbee10fc9e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Apr 2021 21:00:12 +0200 Subject: [PATCH 12/18] Update example for rand --- embassy-net-examples/src/main.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-net-examples/src/main.rs b/embassy-net-examples/src/main.rs index c157ea34e..d1c2658dd 100644 --- a/embassy-net-examples/src/main.rs +++ b/embassy-net-examples/src/main.rs @@ -80,6 +80,12 @@ async fn main_task(spawner: Spawner) { } } +#[no_mangle] +fn _embassy_rand(buf: &mut [u8]) { + use rand_core::{OsRng, RngCore}; + OsRng.fill_bytes(buf); +} + static EXECUTOR: Forever = Forever::new(); fn main() { From 4f528d8fae0c57fad5c5a57f5a61fd0fc4435044 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Apr 2021 21:00:23 +0200 Subject: [PATCH 13/18] Add medium-ip, medium-ethernet Cargo features --- ci.sh | 9 ++++++--- embassy-net-examples/Cargo.toml | 5 +++-- embassy-net/Cargo.toml | 14 ++++---------- embassy-net/src/lib.rs | 7 ++++--- embassy-net/src/stack.rs | 23 +++++++++++++++++++---- 5 files changed, 36 insertions(+), 22 deletions(-) diff --git a/ci.sh b/ci.sh index 385379dec..f43ea5e1a 100755 --- a/ci.sh +++ b/ci.sh @@ -3,11 +3,14 @@ set -euxo pipefail # build for std -(cd embassy-net; cargo build --features log) +(cd embassy-net; cargo build --no-default-features --features log,medium-ethernet,tcp) +(cd embassy-net; cargo build --no-default-features --features log,medium-ethernet,tcp,dhcpv4) +(cd embassy-net; cargo build --no-default-features --features log,medium-ip,tcp) +(cd embassy-net; cargo build --no-default-features --features log,medium-ethernet,medium-ip,tcp,dhcpv4) # build for embedded -(cd embassy-net; cargo build --target thumbv7em-none-eabi --features log) -(cd embassy-net; cargo build --target thumbv7em-none-eabi --features defmt,smoltcp/defmt) +(cd embassy-net; cargo build --target thumbv7em-none-eabi --no-default-features --features log,medium-ethernet,medium-ip,tcp,dhcpv4) +(cd embassy-net; cargo build --target thumbv7em-none-eabi --no-default-features --features defmt,smoltcp/defmt,medium-ethernet,medium-ip,tcp,dhcpv4) # build examples (cd embassy-net-examples; cargo build) diff --git a/embassy-net-examples/Cargo.toml b/embassy-net-examples/Cargo.toml index 796c44745..0a63a3bbd 100644 --- a/embassy-net-examples/Cargo.toml +++ b/embassy-net-examples/Cargo.toml @@ -8,11 +8,12 @@ edition = "2018" heapless = { version = "0.5.6", default-features = false } embassy = { version = "0.1.0", features=["std", "log"] } embassy-std = { version = "0.1.0" } -embassy-net = { version = "0.1.0", path = "../embassy-net", features=["std", "log", "tcp", "dhcpv4"] } +embassy-net = { version = "0.1.0", path = "../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4"] } env_logger = "0.8.2" log = "0.4.11" futures = "0.3.8" libc = "0.2.81" async-io = "1.3.1" smoltcp = { version = "0.7.0", default-features = false } -clap = { version = "3.0.0-beta.2", features = ["derive"] } \ No newline at end of file +clap = { version = "3.0.0-beta.2", features = ["derive"] } +rand_core = { version = "0.6.0", features = ["std"] } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index dccd7984d..b419250be 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -13,7 +13,9 @@ defmt-warn = [] defmt-error = [] tcp = ["smoltcp/socket-tcp"] -dhcpv4 = ["smoltcp/socket-dhcpv4"] +dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"] +medium-ethernet = ["smoltcp/medium-ethernet"] +medium-ip = ["smoltcp/medium-ip"] [dependencies] @@ -36,15 +38,7 @@ version = "0.7.0" #rev = "00952e2c5cdf5667a1dfb6142258055f58d3851c" default-features = false features = [ - "medium-ethernet", - "medium-ip", "proto-ipv4", - #"proto-dhcpv4", - #"proto-igmp", - #"proto-ipv6", - #"socket-raw", - #"socket-icmp", - #"socket-udp", - #"socket-tcp", + "socket", "async", ] diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index edfb2c26d..efa5f3cb1 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -12,9 +12,10 @@ mod device; mod packet_pool; mod stack; -pub use config::{ - Config, Configurator, DhcpConfigurator, Event as ConfigEvent, StaticConfigurator, -}; +#[cfg(feature = "dhcpv4")] +pub use config::DhcpConfigurator; +pub use config::{Config, Configurator, Event as ConfigEvent, StaticConfigurator}; + pub use device::{Device, LinkState}; pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf}; pub use stack::{init, is_init, run}; diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 9b0dd54d4..83cd71707 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -6,12 +6,16 @@ use embassy::time::{Instant, Timer}; use embassy::util::ThreadModeMutex; use embassy::util::{Forever, WakerRegistration}; use futures::pin_mut; -use smoltcp::iface::{InterfaceBuilder, Neighbor, NeighborCache, Route, Routes}; +use smoltcp::iface::InterfaceBuilder; +#[cfg(feature = "medium-ethernet")] +use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; use smoltcp::phy::Device as _; use smoltcp::phy::Medium; use smoltcp::socket::SocketSetItem; use smoltcp::time::Instant as SmolInstant; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; +#[cfg(feature = "medium-ethernet")] +use smoltcp::wire::EthernetAddress; +use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; use crate::config::Configurator; use crate::config::Event; @@ -27,9 +31,12 @@ const LOCAL_PORT_MAX: u16 = 65535; struct StackResources { addresses: [IpCidr; ADDRESSES_LEN], - neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR_CACHE_LEN], sockets: [Option>; SOCKETS_LEN], + + #[cfg(feature = "medium-ethernet")] routes: [Option<(IpCidr, Route)>; 1], + #[cfg(feature = "medium-ethernet")] + neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR_CACHE_LEN], } static STACK_RESOURCES: Forever = Forever::new(); @@ -79,6 +86,7 @@ impl Stack { debug!(" IP address: {}", config.address); set_ipv4_addr(&mut self.iface, config.address); + #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { if let Some(gateway) = config.gateway { debug!(" Default gateway: {}", gateway); @@ -98,6 +106,7 @@ impl Stack { Event::Deconfigured => { debug!("Lost IP configuration"); set_ipv4_addr(&mut self.iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); + #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { self.iface.routes_mut().remove_default_ipv4_route(); } @@ -156,12 +165,17 @@ pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Conf const NONE_SOCKET: Option> = None; let res = STACK_RESOURCES.put(StackResources { addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32)], - neighbor_cache: [None; NEIGHBOR_CACHE_LEN], sockets: [NONE_SOCKET; SOCKETS_LEN], + + #[cfg(feature = "medium-ethernet")] routes: [None; 1], + #[cfg(feature = "medium-ethernet")] + neighbor_cache: [None; NEIGHBOR_CACHE_LEN], }); let medium = device.capabilities().medium; + + #[cfg(feature = "medium-ethernet")] let ethernet_addr = if medium == Medium::Ethernet { device.ethernet_address() } else { @@ -171,6 +185,7 @@ pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Conf let mut b = InterfaceBuilder::new(DeviceAdapter::new(device)); b = b.ip_addrs(&mut res.addresses[..]); + #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { b = b.ethernet_addr(EthernetAddress(ethernet_addr)); b = b.neighbor_cache(NeighborCache::new(&mut res.neighbor_cache[..])); From 4eecb3cfa982abfd4fe97108f14d15236addb9b2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 13 Apr 2021 17:14:23 +0200 Subject: [PATCH 14/18] add is_link_up, is_config_up --- embassy-net/src/lib.rs | 2 +- embassy-net/src/stack.rs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index efa5f3cb1..13f14b836 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -18,7 +18,7 @@ pub use config::{Config, Configurator, Event as ConfigEvent, StaticConfigurator} pub use device::{Device, LinkState}; pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf}; -pub use stack::{init, is_init, run}; +pub use stack::{init, is_config_up, is_init, is_link_up, run}; #[cfg(feature = "tcp")] mod tcp_socket; diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 83cd71707..e436beb1e 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -46,6 +46,7 @@ pub(crate) struct Stack { iface: Interface, pub sockets: SocketSet, link_up: bool, + config_up: bool, next_local_port: u16, configurator: &'static mut dyn Configurator, waker: WakerRegistration, @@ -102,6 +103,8 @@ impl Stack { for (i, s) in config.dns_servers.iter().enumerate() { debug!(" DNS server {}: {}", i, s); } + + self.config_up = true; } Event::Deconfigured => { debug!("Lost IP configuration"); @@ -110,6 +113,7 @@ impl Stack { if medium == Medium::Ethernet { self.iface.routes_mut().remove_default_ipv4_route(); } + self.config_up = false; } } } @@ -209,6 +213,7 @@ pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Conf iface, sockets, link_up: false, + config_up: false, configurator, next_local_port: local_port, waker: WakerRegistration::new(), @@ -221,6 +226,14 @@ pub fn is_init() -> bool { STACK.borrow().borrow().is_some() } +pub fn is_link_up() -> bool { + STACK.borrow().borrow().as_ref().unwrap().link_up +} + +pub fn is_config_up() -> bool { + STACK.borrow().borrow().as_ref().unwrap().config_up +} + pub async fn run() { futures::future::poll_fn(|cx| { Stack::with(|stack| stack.poll(cx)); From 773c3570e76ad6a6e2b8b8d4984cd658c2470843 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 18 May 2021 17:07:05 +0200 Subject: [PATCH 15/18] Update deps --- embassy-net/Cargo.toml | 2 +- embassy-net/src/lib.rs | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index b419250be..36f64dbd4 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -30,7 +30,7 @@ as-slice = { version = "0.1.4" } generic-array = { version = "0.14.4", default-features = false } stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.5", default-features = false, features = [ "async-await" ]} -atomic-pool = "0.1.0" +atomic-pool = "0.2.0" [dependencies.smoltcp] version = "0.7.0" diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 13f14b836..88dcf0aa5 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,8 +1,4 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![feature(const_fn)] -#![feature(const_generics)] -#![feature(const_evaluatable_checked)] -#![allow(incomplete_features)] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; From b4c11695cf43dcdd0aa7a35798a15c923c7844c4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 May 2021 22:34:07 +0200 Subject: [PATCH 16/18] Update smoltcp --- Cargo.toml | 2 +- embassy-net/src/config/dhcp.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8ed84db99..5061494df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,4 +46,4 @@ embassy = { git = "https://github.com/embassy-rs/embassy" } embassy-std = { git = "https://github.com/embassy-rs/embassy" } embassy-macros = { git = "https://github.com/embassy-rs/embassy" } embassy-traits = { git = "https://github.com/embassy-rs/embassy" } -smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="9ce3d9505ef5455bb049713b9262561d78ebf330" } \ No newline at end of file +smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="ec59aba5e10cf91df0c9253d9c2aca4dd143d2ff" } \ No newline at end of file diff --git a/embassy-net/src/config/dhcp.rs b/embassy-net/src/config/dhcp.rs index 007c398b3..f0c144321 100644 --- a/embassy-net/src/config/dhcp.rs +++ b/embassy-net/src/config/dhcp.rs @@ -38,9 +38,9 @@ impl Configurator for DhcpConfigurator { } match socket.poll() { - Dhcpv4Event::NoChange => Event::NoChange, - Dhcpv4Event::Deconfigured => Event::Deconfigured, - Dhcpv4Event::Configured(config) => { + None => Event::NoChange, + Some(Dhcpv4Event::Deconfigured) => Event::Deconfigured, + Some(Dhcpv4Event::Configured(config)) => { let mut dns_servers = Vec::new(); for s in &config.dns_servers { if let Some(addr) = s { From f55a30381448732ebee5c50bf768f1c28ff63628 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 May 2021 23:10:15 +0200 Subject: [PATCH 17/18] net: fix build --- embassy-net-examples/Cargo.toml | 6 +++--- embassy-net/Cargo.toml | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/embassy-net-examples/Cargo.toml b/embassy-net-examples/Cargo.toml index 0a63a3bbd..413428bf6 100644 --- a/embassy-net-examples/Cargo.toml +++ b/embassy-net-examples/Cargo.toml @@ -6,14 +6,14 @@ edition = "2018" [dependencies] heapless = { version = "0.5.6", default-features = false } -embassy = { version = "0.1.0", features=["std", "log"] } -embassy-std = { version = "0.1.0" } +embassy = { version = "0.1.0", path = "../embassy", features=["std", "log"] } +embassy-std = { version = "0.1.0", path = "../embassy-std" } embassy-net = { version = "0.1.0", path = "../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4"] } env_logger = "0.8.2" log = "0.4.11" futures = "0.3.8" libc = "0.2.81" async-io = "1.3.1" -smoltcp = { version = "0.7.0", default-features = false } +smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="ec59aba5e10cf91df0c9253d9c2aca4dd143d2ff", default-features = false } clap = { version = "3.0.0-beta.2", features = ["derive"] } rand_core = { version = "0.6.0", features = ["std"] } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 36f64dbd4..30970c371 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -22,7 +22,7 @@ medium-ip = ["smoltcp/medium-ip"] defmt = { version = "0.2.0", optional = true } log = { version = "0.4.11", optional = true } -embassy = { version = "0.1.0" } +embassy = { version = "0.1.0", path = "../embassy" } managed = { version = "0.8.0", default-features = false, features = [ "map" ]} heapless = { version = "0.5.6", default-features = false } @@ -33,9 +33,8 @@ futures = { version = "0.3.5", default-features = false, features = atomic-pool = "0.2.0" [dependencies.smoltcp] -version = "0.7.0" -#git = "https://github.com/akiles/smoltcp" -#rev = "00952e2c5cdf5667a1dfb6142258055f58d3851c" +git = "https://github.com/smoltcp-rs/smoltcp" +rev = "ec59aba5e10cf91df0c9253d9c2aca4dd143d2ff" default-features = false features = [ "proto-ipv4", From 99048b91e96039298e224b25966c3fe21123a23c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 May 2021 23:13:23 +0200 Subject: [PATCH 18/18] net: add ci --- .github/workflows/rust.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index dfb6a63ff..cef786143 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,6 +26,10 @@ jobs: - package: embassy target: thumbv6m-none-eabi features: defmt + - package: embassy-std-examples + target: x86_64-unknown-linux-gnu + - package: embassy-net-examples + target: x86_64-unknown-linux-gnu - package: embassy-nrf-examples target: thumbv7em-none-eabi - package: embassy-nrf