From 72b0379125b87bcd274bdb81127dd5f0ab29d661 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 9 May 2023 01:51:08 +0200 Subject: [PATCH 1/9] :rainbow: --- .gitignore | 4 + .vscode/settings.json | 5 + Cargo.toml | 26 +++++ LICENSE-APACHE | 201 ++++++++++++++++++++++++++++++++ LICENSE-MIT | 25 ++++ README.md | 7 ++ examples/.cargo/config.toml | 8 ++ examples/Cargo.toml | 70 +++++++++++ examples/README.md | 33 ++++++ examples/build.rs | 36 ++++++ examples/memory.x | 5 + examples/src/bin/multisocket.rs | 148 +++++++++++++++++++++++ examples/src/bin/tcp-client.rs | 128 ++++++++++++++++++++ examples/src/bin/tcp-server.rs | 136 +++++++++++++++++++++ examples/src/bin/udp.rs | 123 +++++++++++++++++++ rust-toolchain.toml | 8 ++ src/device.rs | 161 +++++++++++++++++++++++++ src/lib.rs | 114 ++++++++++++++++++ src/socket.rs | 114 ++++++++++++++++++ src/spi.rs | 37 ++++++ 20 files changed, 1389 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 examples/.cargo/config.toml create mode 100644 examples/Cargo.toml create mode 100644 examples/README.md create mode 100644 examples/build.rs create mode 100644 examples/memory.x create mode 100644 examples/src/bin/multisocket.rs create mode 100644 examples/src/bin/tcp-client.rs create mode 100644 examples/src/bin/tcp-server.rs create mode 100644 examples/src/bin/udp.rs create mode 100644 rust-toolchain.toml create mode 100644 src/device.rs create mode 100644 src/lib.rs create mode 100644 src/socket.rs create mode 100644 src/spi.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..a2ac3d82e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.iml +**/target +**/*.rs.bk +Cargo.lock diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..11fb40927 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "rust-analyzer.linkedProjects": [ + ".\\examples\\Cargo.toml" + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..ac2257f44 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "embassy-net-w5500" +version = "0.1.0" +description = "embassy-net driver for the W5500 ethernet chip" +keywords = ["embedded", "w5500", "embassy-net", "embedded-hal-async", "ethernet", "async"] +categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] +license = "MIT OR Apache-2.0" +edition = "2021" + +[dependencies] +embedded-hal = { version = "1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } +embassy-net-driver-channel = { version = "0.1.0" } +embassy-time = { version = "0.1.0" } +embassy-futures = { version = "0.1.0" } +defmt = { version = "0.3", optional = true } + +[patch.crates-io] +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000..ea4fa15c9 --- /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 2019-2022 Embassy project contributors + +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..87c052836 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2019-2022 Embassy project contributors + +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..9eaf4b700 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# WIZnet W5500 `embassy-net` integration + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet W5500 SPI ethernet chip, operating in MACRAW mode. + +Supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async) + +See [`examples`](https://github.com/kalkyl/embassy-net-w5500/tree/main/examples) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module. \ No newline at end of file diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml new file mode 100644 index 000000000..e6b6b4a41 --- /dev/null +++ b/examples/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-rs-cli run --chip RP2040" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "info" diff --git a/examples/Cargo.toml b/examples/Cargo.toml new file mode 100644 index 000000000..013a2755f --- /dev/null +++ b/examples/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "embassy-net-w5500-examples" +version = "0.1.0" +edition = "2021" + +[dependencies] +embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } +embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } +embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } +embassy-sync = { version = "0.1.0" } +embassy-futures = { version = "0.1.0" } +embassy-net-driver = { version = "0.1.0" } +embassy-net-driver-channel = { version = "0.1.0" } +atomic-polyfill = "0.1.5" +static_cell = "1.0" + +defmt = "=0.3.2" +defmt-rtt = "0.3" +panic-probe = { version = "0.3", features = ["print-defmt"] } +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" + +embedded-io = { version = "0.4.0", features = ["async", "defmt"] } +heapless = "0.7.15" +embedded-hal = { version = "1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } +rand = { version = "0.8.5", default-features = false } + +embassy-net-w5500 = { path = "../" } + +[patch.crates-io] +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } + +[profile.dev] +debug = 2 +debug-assertions = true +opt-level = 1 +overflow-checks = true + +[profile.release] +codegen-units = 1 +debug = 1 +debug-assertions = false +incremental = false +lto = 'fat' +opt-level = 'z' +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 diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..d818c4a89 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,33 @@ +# Examples for the rp2040 `WIZnet W5500-EVB-Pico` board + +Examples are written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. + +## Prerequisites +```bash +cargo install probe-rs-cli +``` + +## TCP server example +```bash +cargo run --bin tcp-server --release +``` +This example implements a TCP echo server on port 1234 and using DHCP. +Send it some data, you should see it echoed back and printed in the console. + +## Multi-socket example +```bash +cargo run --bin multisocket --release +``` +This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. + +## TCP client example +```bash +cargo run --bin tcp-client --release +``` +This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. + +## UDP server example +```bash +cargo run --bin udp --release +``` +This example implements a UDP server listening on port 1234 and echoing back the data. diff --git a/examples/build.rs b/examples/build.rs new file mode 100644 index 000000000..3f915f931 --- /dev/null +++ b/examples/build.rs @@ -0,0 +1,36 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tlink-rp.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/memory.x b/examples/memory.x new file mode 100644 index 000000000..eb8c1731d --- /dev/null +++ b/examples/memory.x @@ -0,0 +1,5 @@ +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 1024K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} \ No newline at end of file diff --git a/examples/src/bin/multisocket.rs b/examples/src/bin/multisocket.rs new file mode 100644 index 000000000..49bcbdbb0 --- /dev/null +++ b/examples/src/bin/multisocket.rs @@ -0,0 +1,148 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::yield_now; +use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; +use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embedded_hal_async::spi::ExclusiveDevice; +use embedded_io::asynch::Write; +use rand::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + ExclusiveDevice, Output<'static, PIN_17>>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = singleton!(State::<8, 8>::new()); + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + embassy_net::Config::Dhcp(Default::default()), + singleton!(StackResources::<3>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Waiting for DHCP..."); + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + // Create two sockets listening to the same port, to handle simultaneous connections + unwrap!(spawner.spawn(listen_task(&stack, 0, 1234))); + unwrap!(spawner.spawn(listen_task(&stack, 1, 1234))); +} + +#[embassy_executor::task(pool_size = 2)] +async fn listen_task(stack: &'static Stack>, id: u8, port: u16) { + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + loop { + let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + info!("SOCKET {}: Listening on TCP:{}...", id, port); + if let Err(e) = socket.accept(port).await { + warn!("accept error: {:?}", e); + continue; + } + info!( + "SOCKET {}: Received connection from {:?}", + id, + socket.remote_endpoint() + ); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("SOCKET {}: {:?}", id, e); + break; + } + }; + info!( + "SOCKET {}: rxd {}", + id, + core::str::from_utf8(&buf[..n]).unwrap() + ); + + if let Err(e) = socket.write_all(&buf[..n]).await { + warn!("write error: {:?}", e); + break; + } + } + } +} + +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { + loop { + if let Some(config) = stack.config() { + return config.clone(); + } + yield_now().await; + } +} diff --git a/examples/src/bin/tcp-client.rs b/examples/src/bin/tcp-client.rs new file mode 100644 index 000000000..32dfb6a68 --- /dev/null +++ b/examples/src/bin/tcp-client.rs @@ -0,0 +1,128 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::str::FromStr; +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::yield_now; +use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; +use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::spi::ExclusiveDevice; +use embedded_io::asynch::Write; +use rand::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + ExclusiveDevice, Output<'static, PIN_17>>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + let mut led = Output::new(p.PIN_25, Level::Low); + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = singleton!(State::<8, 8>::new()); + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + embassy_net::Config::Dhcp(Default::default()), + singleton!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Waiting for DHCP..."); + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + loop { + let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + led.set_low(); + info!("Connecting..."); + let host_addr = embassy_net::Ipv4Address::from_str("192.168.1.110").unwrap(); + if let Err(e) = socket.connect((host_addr, 1234)).await { + warn!("connect error: {:?}", e); + continue; + } + info!("Connected to {:?}", socket.remote_endpoint()); + led.set_high(); + + let msg = b"Hello world!\n"; + loop { + if let Err(e) = socket.write_all(msg).await { + warn!("write error: {:?}", e); + break; + } + info!("txd: {}", core::str::from_utf8(msg).unwrap()); + Timer::after(Duration::from_secs(1)).await; + } + } +} + +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { + loop { + if let Some(config) = stack.config() { + return config.clone(); + } + yield_now().await; + } +} diff --git a/examples/src/bin/tcp-server.rs b/examples/src/bin/tcp-server.rs new file mode 100644 index 000000000..04b220146 --- /dev/null +++ b/examples/src/bin/tcp-server.rs @@ -0,0 +1,136 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::yield_now; +use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; +use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embedded_hal_async::spi::ExclusiveDevice; +use embedded_io::asynch::Write; +use rand::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + ExclusiveDevice, Output<'static, PIN_17>>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + let mut led = Output::new(p.PIN_25, Level::Low); + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = singleton!(State::<8, 8>::new()); + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + embassy_net::Config::Dhcp(Default::default()), + singleton!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Waiting for DHCP..."); + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + loop { + let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + led.set_low(); + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + info!("Received connection from {:?}", socket.remote_endpoint()); + led.set_high(); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("{:?}", e); + break; + } + }; + info!("rxd {}", core::str::from_utf8(&buf[..n]).unwrap()); + + if let Err(e) = socket.write_all(&buf[..n]).await { + warn!("write error: {:?}", e); + break; + } + } + } +} + +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { + loop { + if let Some(config) = stack.config() { + return config.clone(); + } + yield_now().await; + } +} diff --git a/examples/src/bin/udp.rs b/examples/src/bin/udp.rs new file mode 100644 index 000000000..4dc5e1f2f --- /dev/null +++ b/examples/src/bin/udp.rs @@ -0,0 +1,123 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::yield_now; +use embassy_net::udp::UdpSocket; +use embassy_net::{PacketMetadata, Stack, StackResources}; +use embassy_net_w5500::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; +use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embedded_hal_async::spi::ExclusiveDevice; +use rand::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + ExclusiveDevice, Output<'static, PIN_17>>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = singleton!(State::<8, 8>::new()); + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + embassy_net::Config::Dhcp(Default::default()), + singleton!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Waiting for DHCP..."); + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + // Then we can use it! + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut rx_meta = [PacketMetadata::EMPTY; 16]; + let mut tx_meta = [PacketMetadata::EMPTY; 16]; + let mut buf = [0; 4096]; + loop { + let mut socket = UdpSocket::new( + stack, + &mut rx_meta, + &mut rx_buffer, + &mut tx_meta, + &mut tx_buffer, + ); + socket.bind(1234).unwrap(); + + loop { + let (n, ep) = socket.recv_from(&mut buf).await.unwrap(); + if let Ok(s) = core::str::from_utf8(&buf[..n]) { + info!("rxd from {}: {}", ep, s); + } + socket.send_to(&buf[..n], ep).await.unwrap(); + } + } +} + +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { + loop { + if let Some(config) = stack.config() { + return config.clone(); + } + yield_now().await; + } +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..fb284c1ee --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,8 @@ +# Before upgrading check that everything is available on all tier1 targets here: +# https://rust-lang.github.io/rustup-components-history +[toolchain] +channel = "nightly-2023-04-04" +components = [ "rust-src", "rustfmt" ] +targets = [ + "thumbv6m-none-eabi", +] diff --git a/src/device.rs b/src/device.rs new file mode 100644 index 000000000..3875fde0e --- /dev/null +++ b/src/device.rs @@ -0,0 +1,161 @@ +use crate::socket; +use crate::spi::SpiInterface; +use embedded_hal_async::spi::SpiDevice; + +pub const MODE: u16 = 0x00; +pub const MAC: u16 = 0x09; +pub const SOCKET_INTR: u16 = 0x18; +pub const PHY_CFG: u16 = 0x2E; + +#[repr(u8)] +pub enum RegisterBlock { + Common = 0x00, + Socket0 = 0x01, + TxBuf = 0x02, + RxBuf = 0x03, +} + +/// W5500 in MACRAW mode +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct W5500 { + bus: SpiInterface, +} + +impl W5500 { + /// Create and initialize the W5500 driver + pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { + let mut bus = SpiInterface(spi); + // Reset device + bus.write_frame(RegisterBlock::Common, MODE, &[0x80]) + .await?; + + // Enable interrupt pin + bus.write_frame(RegisterBlock::Common, SOCKET_INTR, &[0x01]) + .await?; + // Enable receive interrupt + bus.write_frame( + RegisterBlock::Socket0, + socket::SOCKET_INTR_MASK, + &[socket::Interrupt::Receive as u8], + ) + .await?; + + // Set MAC address + bus.write_frame(RegisterBlock::Common, MAC, &mac_addr) + .await?; + + // Set the raw socket RX/TX buffer sizes to 16KB + bus.write_frame(RegisterBlock::Socket0, socket::TXBUF_SIZE, &[16]) + .await?; + bus.write_frame(RegisterBlock::Socket0, socket::RXBUF_SIZE, &[16]) + .await?; + + // MACRAW mode with MAC filtering. + let mode: u8 = (1 << 2) | (1 << 7); + bus.write_frame(RegisterBlock::Socket0, socket::MODE, &[mode]) + .await?; + socket::command(&mut bus, socket::Command::Open).await?; + + Ok(Self { bus }) + } + + /// Read bytes from the RX buffer. Returns the number of bytes read. + async fn read_bytes(&mut self, buffer: &mut [u8], offset: u16) -> Result { + let rx_size = socket::get_rx_size(&mut self.bus).await? as usize; + + let read_buffer = if rx_size > buffer.len() + offset as usize { + buffer + } else { + &mut buffer[..rx_size - offset as usize] + }; + + let read_ptr = socket::get_rx_read_ptr(&mut self.bus) + .await? + .wrapping_add(offset); + self.bus + .read_frame(RegisterBlock::RxBuf, read_ptr, read_buffer) + .await?; + socket::set_rx_read_ptr( + &mut self.bus, + read_ptr.wrapping_add(read_buffer.len() as u16), + ) + .await?; + + Ok(read_buffer.len()) + } + + /// Read an ethernet frame from the device. Returns the number of bytes read. + pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result { + let rx_size = socket::get_rx_size(&mut self.bus).await? as usize; + if rx_size == 0 { + return Ok(0); + } + + socket::reset_interrupt(&mut self.bus, socket::Interrupt::Receive).await?; + + // First two bytes gives the size of the received ethernet frame + let expected_frame_size: usize = { + let mut frame_bytes = [0u8; 2]; + assert!(self.read_bytes(&mut frame_bytes[..], 0).await? == 2); + u16::from_be_bytes(frame_bytes) as usize - 2 + }; + + // Read the ethernet frame + let read_buffer = if frame.len() > expected_frame_size { + &mut frame[..expected_frame_size] + } else { + frame + }; + + let recvd_frame_size = self.read_bytes(read_buffer, 2).await?; + + // Register RX as completed + socket::command(&mut self.bus, socket::Command::Receive).await?; + + // If the whole frame wasn't read, drop it + if recvd_frame_size < expected_frame_size { + Ok(0) + } else { + Ok(recvd_frame_size) + } + } + + /// Write an ethernet frame to the device. Returns number of bytes written + pub async fn write_frame(&mut self, frame: &[u8]) -> Result { + let max_size = socket::get_tx_free_size(&mut self.bus).await? as usize; + + let write_data = if frame.len() < max_size { + frame + } else { + &frame[..max_size] + }; + + let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?; + self.bus + .write_frame(RegisterBlock::TxBuf, write_ptr, write_data) + .await?; + socket::set_tx_write_ptr( + &mut self.bus, + write_ptr.wrapping_add(write_data.len() as u16), + ) + .await?; + + socket::reset_interrupt(&mut self.bus, socket::Interrupt::SendOk).await?; + socket::command(&mut self.bus, socket::Command::Send).await?; + // Wait for TX to complete + while !socket::is_interrupt(&mut self.bus, socket::Interrupt::SendOk).await? {} + socket::reset_interrupt(&mut self.bus, socket::Interrupt::SendOk).await?; + + Ok(write_data.len()) + } + + pub async fn is_link_up(&mut self) -> bool { + let mut link = [0]; + self.bus + .read_frame(RegisterBlock::Common, PHY_CFG, &mut link) + .await + .ok(); + link[0] & 1 == 1 + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..bf14b05b4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,114 @@ +#![no_std] +/// [`embassy-net`](crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. +mod device; +mod socket; +mod spi; + +use crate::device::W5500; +use embassy_futures::select::{select, Either}; +use embassy_net_driver_channel as ch; +use embassy_net_driver_channel::driver::LinkState; +use embassy_time::{Duration, Timer}; +use embedded_hal::digital::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiDevice; +const MTU: usize = 1514; + +/// Type alias for the embassy-net driver for W5500 +pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; + +/// Internal state for the embassy-net integration. +pub struct State { + ch_state: ch::State, +} + +impl State { + /// Create a new `State`. + pub const fn new() -> Self { + Self { + ch_state: ch::State::new(), + } + } +} + +/// Background runner for the W5500. +/// +/// You must call `.run()` in a background task for the W5500 to operate. +pub struct Runner<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> { + mac: W5500, + ch: ch::Runner<'d, MTU>, + int: INT, + _reset: RST, +} + +/// You must call this in a background task for the W5500 to operate. +impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { + pub async fn run(mut self) -> ! { + let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); + loop { + if self.mac.is_link_up().await { + state_chan.set_link_state(LinkState::Up); + loop { + match select( + async { + self.int.wait_for_low().await.ok(); + rx_chan.rx_buf().await + }, + tx_chan.tx_buf(), + ) + .await + { + Either::First(p) => { + if let Ok(n) = self.mac.read_frame(p).await { + rx_chan.rx_done(n); + } + } + Either::Second(p) => { + self.mac.write_frame(p).await.ok(); + tx_chan.tx_done(); + } + } + } + } else { + state_chan.set_link_state(LinkState::Down); + } + } + } +} + +/// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net). +pub async fn new< + 'a, + const N_RX: usize, + const N_TX: usize, + SPI: SpiDevice, + INT: Wait, + RST: OutputPin, +>( + mac_addr: [u8; 6], + state: &'a mut State, + spi_dev: SPI, + int: INT, + mut reset: RST, +) -> (Device<'a>, Runner<'a, SPI, INT, RST>) { + // Reset the W5500. + reset.set_low().ok(); + // Ensure the reset is registered. + Timer::after(Duration::from_millis(1)).await; + reset.set_high().ok(); + // Wait for the W5500 to achieve PLL lock. + Timer::after(Duration::from_millis(2)).await; + + let mac = W5500::new(spi_dev, mac_addr).await.unwrap(); + + let (runner, device) = ch::new(&mut state.ch_state, mac_addr); + ( + device, + Runner { + ch: runner, + mac, + int, + _reset: reset, + }, + ) +} diff --git a/src/socket.rs b/src/socket.rs new file mode 100644 index 000000000..0d3d1aeb2 --- /dev/null +++ b/src/socket.rs @@ -0,0 +1,114 @@ +use crate::device::RegisterBlock; +use crate::spi::SpiInterface; +use embedded_hal_async::spi::SpiDevice; + +pub const MODE: u16 = 0x00; +pub const COMMAND: u16 = 0x01; +pub const RXBUF_SIZE: u16 = 0x1E; +pub const TXBUF_SIZE: u16 = 0x1F; +pub const TX_FREE_SIZE: u16 = 0x20; +pub const TX_DATA_WRITE_PTR: u16 = 0x24; +pub const RECVD_SIZE: u16 = 0x26; +pub const RX_DATA_READ_PTR: u16 = 0x28; +pub const SOCKET_INTR_MASK: u16 = 0x2C; + +#[repr(u8)] +pub enum Command { + Open = 0x01, + Send = 0x20, + Receive = 0x40, +} + +pub const INTR: u16 = 0x02; +#[repr(u8)] +pub enum Interrupt { + SendOk = 0b010000_u8, + Receive = 0b00100_u8, +} + +pub async fn reset_interrupt( + bus: &mut SpiInterface, + code: Interrupt, +) -> Result<(), SPI::Error> { + let data = [code as u8]; + bus.write_frame(RegisterBlock::Socket0, INTR, &data).await +} + +pub async fn is_interrupt( + bus: &mut SpiInterface, + code: Interrupt, +) -> Result { + let mut data = [0u8]; + bus.read_frame(RegisterBlock::Socket0, INTR, &mut data) + .await?; + Ok(data[0] & code as u8 != 0) +} + +pub async fn get_tx_write_ptr( + bus: &mut SpiInterface, +) -> Result { + let mut data = [0u8; 2]; + bus.read_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &mut data) + .await?; + Ok(u16::from_be_bytes(data)) +} + +pub async fn set_tx_write_ptr( + bus: &mut SpiInterface, + ptr: u16, +) -> Result<(), SPI::Error> { + let data = ptr.to_be_bytes(); + bus.write_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &data) + .await +} + +pub async fn get_rx_read_ptr( + bus: &mut SpiInterface, +) -> Result { + let mut data = [0u8; 2]; + bus.read_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &mut data) + .await?; + Ok(u16::from_be_bytes(data)) +} + +pub async fn set_rx_read_ptr( + bus: &mut SpiInterface, + ptr: u16, +) -> Result<(), SPI::Error> { + let data = ptr.to_be_bytes(); + bus.write_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &data) + .await +} + +pub async fn command( + bus: &mut SpiInterface, + command: Command, +) -> Result<(), SPI::Error> { + let data = [command as u8]; + bus.write_frame(RegisterBlock::Socket0, COMMAND, &data) + .await +} + +pub async fn get_rx_size(bus: &mut SpiInterface) -> Result { + loop { + // Wait until two sequential reads are equal + let mut res0 = [0u8; 2]; + bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res0) + .await?; + let mut res1 = [0u8; 2]; + bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res1) + .await?; + if res0 == res1 { + break Ok(u16::from_be_bytes(res0)); + } + } +} + +pub async fn get_tx_free_size( + bus: &mut SpiInterface, +) -> Result { + let mut data = [0; 2]; + bus.read_frame(RegisterBlock::Socket0, TX_FREE_SIZE, &mut data) + .await?; + Ok(u16::from_be_bytes(data)) +} diff --git a/src/spi.rs b/src/spi.rs new file mode 100644 index 000000000..55d311888 --- /dev/null +++ b/src/spi.rs @@ -0,0 +1,37 @@ +use crate::device::RegisterBlock; +use embedded_hal_async::spi::{Operation, SpiDevice}; + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct SpiInterface(pub SPI); + +impl SpiInterface { + pub async fn read_frame( + &mut self, + block: RegisterBlock, + address: u16, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + let address_phase = address.to_be_bytes(); + let control_phase = [(block as u8) << 3]; + let operations = &mut [ + Operation::Write(&address_phase), + Operation::Write(&control_phase), + Operation::TransferInPlace(data), + ]; + self.0.transaction(operations).await + } + + pub async fn write_frame( + &mut self, + block: RegisterBlock, + address: u16, + data: &[u8], + ) -> Result<(), SPI::Error> { + let address_phase = address.to_be_bytes(); + let control_phase = [(block as u8) << 3 | 0b0000_0100]; + let data_phase = data; + let operations = &[&address_phase[..], &control_phase, &data_phase]; + self.0.write_transaction(operations).await + } +} From fdc87a8e7f70e5246ab608df931a9623fb0289b4 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 9 May 2023 02:21:17 +0200 Subject: [PATCH 2/9] Add CI --- .github/workflows/rust.yml | 29 +++++++++++++++++++++++++++++ ci.sh | 18 ++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 .github/workflows/rust.yml create mode 100644 ci.sh diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 000000000..dfa96dd0e --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,29 @@ +name: Rust + +on: + push: + branches: [main] + pull_request: + branches: [main] + merge_group: + +env: + CARGO_TERM_COLOR: always + +jobs: + build-nightly: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Check fmt + run: cargo fmt -- --check + - name: Build + run: ./ci.sh diff --git a/ci.sh b/ci.sh new file mode 100644 index 000000000..74610ff28 --- /dev/null +++ b/ci.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euxo pipefail + +export DEFMT_LOG=trace + +# build examples +#================== + +(cd examples; cargo build --bin multisocket --release) +(cd examples; cargo build --bin tcp-client --release) +(cd examples; cargo build --bin tcp-server --release) +(cd examples; cargo build --bin tcp-udp --release) + +# build lib +#============ + +cargo build --target thumbv6m-none-eabi From 9d018a0075ee3390c5fd7c8af5da3bb8a8787c7a Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 9 May 2023 02:24:25 +0200 Subject: [PATCH 3/9] Add CI --- ci.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 ci.sh diff --git a/ci.sh b/ci.sh old mode 100644 new mode 100755 From d40589f08216c1fce6b9c8d8ad74d18fb5e58285 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 9 May 2023 02:27:28 +0200 Subject: [PATCH 4/9] Fix CI --- ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci.sh b/ci.sh index 74610ff28..0a2876987 100755 --- a/ci.sh +++ b/ci.sh @@ -10,7 +10,7 @@ export DEFMT_LOG=trace (cd examples; cargo build --bin multisocket --release) (cd examples; cargo build --bin tcp-client --release) (cd examples; cargo build --bin tcp-server --release) -(cd examples; cargo build --bin tcp-udp --release) +(cd examples; cargo build --bin udp --release) # build lib #============ From adefa4f86bd3f5da3cfe85c618603abba54921ea Mon Sep 17 00:00:00 2001 From: kalkyl Date: Wed, 10 May 2023 02:23:54 +0200 Subject: [PATCH 5/9] vscode settings --- .vscode/settings.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 11fb40927..af1bd7b5c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,15 @@ { + "editor.formatOnSave": true, + "[toml]": { + "editor.formatOnSave": false + }, + "rust-analyzer.check.allTargets": false, + "rust-analyzer.check.noDefaultFeatures": true, + "rust-analyzer.cargo.noDefaultFeatures": true, + "rust-analyzer.cargo.target": "thumbv6m-none-eabi", + "rust-analyzer.cargo.features": [ + "nightly", + ], "rust-analyzer.linkedProjects": [ ".\\examples\\Cargo.toml" ] From 7b83d53bbfb5be30415d966504eacbd6f4c1cc90 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Wed, 10 May 2023 02:25:18 +0200 Subject: [PATCH 6/9] vscode settings --- .vscode/settings.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index af1bd7b5c..231c407ad 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,15 +1,12 @@ { "editor.formatOnSave": true, "[toml]": { - "editor.formatOnSave": false + "editor.formatOnSave": false }, "rust-analyzer.check.allTargets": false, "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.cargo.target": "thumbv6m-none-eabi", - "rust-analyzer.cargo.features": [ - "nightly", - ], "rust-analyzer.linkedProjects": [ ".\\examples\\Cargo.toml" ] From bbd687fcb0e63a1bb8eb4d31c8f5ed2f403603f6 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Wed, 10 May 2023 02:40:41 +0200 Subject: [PATCH 7/9] Update embassy --- Cargo.toml | 16 ++++++++-------- examples/Cargo.toml | 18 +++++++++--------- rust-toolchain.toml | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ac2257f44..1921e812d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,11 @@ embassy-futures = { version = "0.1.0" } defmt = { version = "0.3", optional = true } [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 013a2755f..46659c2bc 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } +embassy-executor = { version = "0.2.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } @@ -30,14 +30,14 @@ rand = { version = "0.8.5", default-features = false } embassy-net-w5500 = { path = "../" } [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } [profile.dev] debug = 2 diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fb284c1ee..2582e88f5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,7 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2023-04-04" +channel = "nightly-2023-04-18" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", From 6c1137177f92f9a5c5cd9c2a0450b5db5165f8be Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 13 May 2023 06:34:03 +0200 Subject: [PATCH 8/9] Wait until there's enough space in tx buffer, remove busy wait for completed send --- src/device.rs | 25 ++++--------------------- src/socket.rs | 11 ----------- 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/src/device.rs b/src/device.rs index 3875fde0e..8158bc98e 100644 --- a/src/device.rs +++ b/src/device.rs @@ -123,31 +123,14 @@ impl W5500 { /// Write an ethernet frame to the device. Returns number of bytes written pub async fn write_frame(&mut self, frame: &[u8]) -> Result { - let max_size = socket::get_tx_free_size(&mut self.bus).await? as usize; - - let write_data = if frame.len() < max_size { - frame - } else { - &frame[..max_size] - }; - + while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {} let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?; self.bus - .write_frame(RegisterBlock::TxBuf, write_ptr, write_data) + .write_frame(RegisterBlock::TxBuf, write_ptr, frame) .await?; - socket::set_tx_write_ptr( - &mut self.bus, - write_ptr.wrapping_add(write_data.len() as u16), - ) - .await?; - - socket::reset_interrupt(&mut self.bus, socket::Interrupt::SendOk).await?; + socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?; socket::command(&mut self.bus, socket::Command::Send).await?; - // Wait for TX to complete - while !socket::is_interrupt(&mut self.bus, socket::Interrupt::SendOk).await? {} - socket::reset_interrupt(&mut self.bus, socket::Interrupt::SendOk).await?; - - Ok(write_data.len()) + Ok(frame.len()) } pub async fn is_link_up(&mut self) -> bool { diff --git a/src/socket.rs b/src/socket.rs index 0d3d1aeb2..3f64d04d1 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -22,7 +22,6 @@ pub enum Command { pub const INTR: u16 = 0x02; #[repr(u8)] pub enum Interrupt { - SendOk = 0b010000_u8, Receive = 0b00100_u8, } @@ -34,16 +33,6 @@ pub async fn reset_interrupt( bus.write_frame(RegisterBlock::Socket0, INTR, &data).await } -pub async fn is_interrupt( - bus: &mut SpiInterface, - code: Interrupt, -) -> Result { - let mut data = [0u8]; - bus.read_frame(RegisterBlock::Socket0, INTR, &mut data) - .await?; - Ok(data[0] & code as u8 != 0) -} - pub async fn get_tx_write_ptr( bus: &mut SpiInterface, ) -> Result { From 7f0e778145e7a0f7281d9b37e59473fddf232097 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 31 May 2023 00:54:20 +0200 Subject: [PATCH 9/9] move embassy-net-w5500 to subdir. --- .github/workflows/rust.yml | 29 --- .gitignore | 4 - .vscode/settings.json | 13 -- LICENSE-APACHE | 201 ------------------ LICENSE-MIT | 25 --- ci.sh | 18 -- Cargo.toml => embassy-net-w5500/Cargo.toml | 0 README.md => embassy-net-w5500/README.md | 0 {src => embassy-net-w5500/src}/device.rs | 0 {src => embassy-net-w5500/src}/lib.rs | 0 {src => embassy-net-w5500/src}/socket.rs | 0 {src => embassy-net-w5500/src}/spi.rs | 0 examples/.cargo/config.toml | 8 - examples/Cargo.toml | 70 ------ examples/README.md | 33 --- examples/build.rs | 36 ---- examples/memory.x | 5 - .../src/bin/ethernet_w5500_multisocket.rs} | 4 + .../src/bin/ethernet_w5500_tcp_client.rs} | 4 + .../src/bin/ethernet_w5500_tcp_server.rs} | 5 + .../src/bin/ethernet_w5500_udp.rs} | 4 + rust-toolchain.toml | 8 - 22 files changed, 17 insertions(+), 450 deletions(-) delete mode 100644 .github/workflows/rust.yml delete mode 100644 .gitignore delete mode 100644 .vscode/settings.json delete mode 100644 LICENSE-APACHE delete mode 100644 LICENSE-MIT delete mode 100755 ci.sh rename Cargo.toml => embassy-net-w5500/Cargo.toml (100%) rename README.md => embassy-net-w5500/README.md (100%) rename {src => embassy-net-w5500/src}/device.rs (100%) rename {src => embassy-net-w5500/src}/lib.rs (100%) rename {src => embassy-net-w5500/src}/socket.rs (100%) rename {src => embassy-net-w5500/src}/spi.rs (100%) delete mode 100644 examples/.cargo/config.toml delete mode 100644 examples/Cargo.toml delete mode 100644 examples/README.md delete mode 100644 examples/build.rs delete mode 100644 examples/memory.x rename examples/{src/bin/multisocket.rs => rp/src/bin/ethernet_w5500_multisocket.rs} (94%) rename examples/{src/bin/tcp-client.rs => rp/src/bin/ethernet_w5500_tcp_client.rs} (93%) rename examples/{src/bin/tcp-server.rs => rp/src/bin/ethernet_w5500_tcp_server.rs} (93%) rename examples/{src/bin/udp.rs => rp/src/bin/ethernet_w5500_udp.rs} (94%) delete mode 100644 rust-toolchain.toml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index dfa96dd0e..000000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Rust - -on: - push: - branches: [main] - pull_request: - branches: [main] - merge_group: - -env: - CARGO_TERM_COLOR: always - -jobs: - build-nightly: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Check fmt - run: cargo fmt -- --check - - name: Build - run: ./ci.sh diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a2ac3d82e..000000000 --- a/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.iml -**/target -**/*.rs.bk -Cargo.lock diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 231c407ad..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "editor.formatOnSave": true, - "[toml]": { - "editor.formatOnSave": false - }, - "rust-analyzer.check.allTargets": false, - "rust-analyzer.check.noDefaultFeatures": true, - "rust-analyzer.cargo.noDefaultFeatures": true, - "rust-analyzer.cargo.target": "thumbv6m-none-eabi", - "rust-analyzer.linkedProjects": [ - ".\\examples\\Cargo.toml" - ] -} \ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE deleted file mode 100644 index ea4fa15c9..000000000 --- a/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - 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 2019-2022 Embassy project contributors - -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 deleted file mode 100644 index 87c052836..000000000 --- a/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2019-2022 Embassy project contributors - -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/ci.sh b/ci.sh deleted file mode 100755 index 0a2876987..000000000 --- a/ci.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -export DEFMT_LOG=trace - -# build examples -#================== - -(cd examples; cargo build --bin multisocket --release) -(cd examples; cargo build --bin tcp-client --release) -(cd examples; cargo build --bin tcp-server --release) -(cd examples; cargo build --bin udp --release) - -# build lib -#============ - -cargo build --target thumbv6m-none-eabi diff --git a/Cargo.toml b/embassy-net-w5500/Cargo.toml similarity index 100% rename from Cargo.toml rename to embassy-net-w5500/Cargo.toml diff --git a/README.md b/embassy-net-w5500/README.md similarity index 100% rename from README.md rename to embassy-net-w5500/README.md diff --git a/src/device.rs b/embassy-net-w5500/src/device.rs similarity index 100% rename from src/device.rs rename to embassy-net-w5500/src/device.rs diff --git a/src/lib.rs b/embassy-net-w5500/src/lib.rs similarity index 100% rename from src/lib.rs rename to embassy-net-w5500/src/lib.rs diff --git a/src/socket.rs b/embassy-net-w5500/src/socket.rs similarity index 100% rename from src/socket.rs rename to embassy-net-w5500/src/socket.rs diff --git a/src/spi.rs b/embassy-net-w5500/src/spi.rs similarity index 100% rename from src/spi.rs rename to embassy-net-w5500/src/spi.rs diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml deleted file mode 100644 index e6b6b4a41..000000000 --- a/examples/.cargo/config.toml +++ /dev/null @@ -1,8 +0,0 @@ -[target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-rs-cli run --chip RP2040" - -[build] -target = "thumbv6m-none-eabi" - -[env] -DEFMT_LOG = "info" diff --git a/examples/Cargo.toml b/examples/Cargo.toml deleted file mode 100644 index 46659c2bc..000000000 --- a/examples/Cargo.toml +++ /dev/null @@ -1,70 +0,0 @@ -[package] -name = "embassy-net-w5500-examples" -version = "0.1.0" -edition = "2021" - -[dependencies] -embassy-executor = { version = "0.2.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } -embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } -embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } -embassy-sync = { version = "0.1.0" } -embassy-futures = { version = "0.1.0" } -embassy-net-driver = { version = "0.1.0" } -embassy-net-driver-channel = { version = "0.1.0" } -atomic-polyfill = "0.1.5" -static_cell = "1.0" - -defmt = "=0.3.2" -defmt-rtt = "0.3" -panic-probe = { version = "0.3", features = ["print-defmt"] } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } -cortex-m-rt = "0.7.0" - -embedded-io = { version = "0.4.0", features = ["async", "defmt"] } -heapless = "0.7.15" -embedded-hal = { version = "1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } -rand = { version = "0.8.5", default-features = false } - -embassy-net-w5500 = { path = "../" } - -[patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } - -[profile.dev] -debug = 2 -debug-assertions = true -opt-level = 1 -overflow-checks = true - -[profile.release] -codegen-units = 1 -debug = 1 -debug-assertions = false -incremental = false -lto = 'fat' -opt-level = 'z' -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 diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index d818c4a89..000000000 --- a/examples/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Examples for the rp2040 `WIZnet W5500-EVB-Pico` board - -Examples are written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. - -## Prerequisites -```bash -cargo install probe-rs-cli -``` - -## TCP server example -```bash -cargo run --bin tcp-server --release -``` -This example implements a TCP echo server on port 1234 and using DHCP. -Send it some data, you should see it echoed back and printed in the console. - -## Multi-socket example -```bash -cargo run --bin multisocket --release -``` -This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. - -## TCP client example -```bash -cargo run --bin tcp-client --release -``` -This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. - -## UDP server example -```bash -cargo run --bin udp --release -``` -This example implements a UDP server listening on port 1234 and echoing back the data. diff --git a/examples/build.rs b/examples/build.rs deleted file mode 100644 index 3f915f931..000000000 --- a/examples/build.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! This build script copies the `memory.x` file from the crate root into -//! a directory where the linker can always find it at build time. -//! For many projects this is optional, as the linker always searches the -//! project root directory -- wherever `Cargo.toml` is. However, if you -//! are using a workspace or have a more complicated build setup, this -//! build script becomes required. Additionally, by requesting that -//! Cargo re-run the build script whenever `memory.x` is changed, -//! updating `memory.x` ensures a rebuild of the application with the -//! new memory settings. - -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; - -fn main() { - // Put `memory.x` in our output directory and ensure it's - // on the linker search path. - let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); - File::create(out.join("memory.x")) - .unwrap() - .write_all(include_bytes!("memory.x")) - .unwrap(); - println!("cargo:rustc-link-search={}", out.display()); - - // By default, Cargo will re-run a build script whenever - // any file in the project changes. By specifying `memory.x` - // here, we ensure the build script is only re-run when - // `memory.x` is changed. - println!("cargo:rerun-if-changed=memory.x"); - - println!("cargo:rustc-link-arg-bins=--nmagic"); - println!("cargo:rustc-link-arg-bins=-Tlink.x"); - println!("cargo:rustc-link-arg-bins=-Tlink-rp.x"); - println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); -} diff --git a/examples/memory.x b/examples/memory.x deleted file mode 100644 index eb8c1731d..000000000 --- a/examples/memory.x +++ /dev/null @@ -1,5 +0,0 @@ -MEMORY { - BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 - FLASH : ORIGIN = 0x10000100, LENGTH = 1024K - 0x100 - RAM : ORIGIN = 0x20000000, LENGTH = 256K -} \ No newline at end of file diff --git a/examples/src/bin/multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs similarity index 94% rename from examples/src/bin/multisocket.rs rename to examples/rp/src/bin/ethernet_w5500_multisocket.rs index 49bcbdbb0..eb3b8de81 100644 --- a/examples/src/bin/multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -1,3 +1,7 @@ +//! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. +//! +//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/src/bin/tcp-client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs similarity index 93% rename from examples/src/bin/tcp-client.rs rename to examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 32dfb6a68..e166e0f35 100644 --- a/examples/src/bin/tcp-client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -1,3 +1,7 @@ +//! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. +//! +//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/src/bin/tcp-server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs similarity index 93% rename from examples/src/bin/tcp-server.rs rename to examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 04b220146..ffd664d15 100644 --- a/examples/src/bin/tcp-server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -1,3 +1,8 @@ +//! This example implements a TCP echo server on port 1234 and using DHCP. +//! Send it some data, you should see it echoed back and printed in the console. +//! +//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/src/bin/udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs similarity index 94% rename from examples/src/bin/udp.rs rename to examples/rp/src/bin/ethernet_w5500_udp.rs index 4dc5e1f2f..08ffeb244 100644 --- a/examples/src/bin/udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -1,3 +1,7 @@ +//! This example implements a UDP server listening on port 1234 and echoing back the data. +//! +//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 2582e88f5..000000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,8 +0,0 @@ -# Before upgrading check that everything is available on all tier1 targets here: -# https://rust-lang.github.io/rustup-components-history -[toolchain] -channel = "nightly-2023-04-18" -components = [ "rust-src", "rustfmt" ] -targets = [ - "thumbv6m-none-eabi", -]