From ce7353fba4e2a133087f0312ae47184aa180642e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 12 Jul 2022 07:52:16 +0200 Subject: [PATCH] Hook up embassy-net. IT WORKS. --- Cargo.toml | 1 + examples/rpi-pico-w/Cargo.toml | 9 ++ examples/rpi-pico-w/src/main.rs | 78 ++++++++++++++- src/lib.rs | 168 ++++++++++++++++++++++++++++++-- src/structs.rs | 9 ++ 5 files changed, 255 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd27a48b..31a14f96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ log = ["dep:log"] [dependencies] embassy = { version = "0.1.0" } embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac"] } +embassy-net = { version = "0.1.0" } atomic-polyfill = "0.1.5" defmt = { version = "0.3", optional = true } diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 8dbcb20d..9e1d7547 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" cyw43 = { path = "../../", features = ["defmt"]} embassy = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } +embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } atomic-polyfill = "0.1.5" defmt = "0.3" @@ -20,13 +21,21 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-io = { version = "0.3.0", features = ["async", "defmt"] } +heapless = "0.7.15" [patch.crates-io] embassy = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } #embassy = { path = "/home/dirbaio/embassy/embassy/embassy" } #embassy-rp = { path = "/home/dirbaio/embassy/embassy/embassy-rp" } +#embassy-net = { path = "/home/dirbaio/embassy/embassy/embassy-net" } +#smoltcp = { path = "./smoltcp" } + +#[patch."https://github.com/smoltcp-rs/smoltcp"] +#smoltcp = { path = "./smoltcp" } [profile.dev] debug = 2 diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 6d161414..e08ee8e9 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -8,9 +8,13 @@ use defmt::{assert, assert_eq, panic, *}; use embassy::executor::Spawner; use embassy::time::{Duration, Timer}; use embassy::util::Forever; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources}; use embassy_rp::gpio::{Flex, Level, Output, Pin}; use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; use embassy_rp::Peripherals; +use embedded_io::asynch::{Read, Write}; +use heapless::Vec; use {defmt_rtt as _, panic_probe as _}; macro_rules! forever { @@ -26,6 +30,11 @@ async fn wifi_task(runner: cyw43::Runner<'static, PIN_23, PIN_25, PIN_29, PIN_24 runner.run().await } +#[embassy::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + #[embassy::main] async fn main(spawner: Spawner, p: Peripherals) { info!("Hello World!"); @@ -45,8 +54,71 @@ async fn main(spawner: Spawner, p: Peripherals) { spawner.spawn(wifi_task(runner)).unwrap(); - control.init().await; + let net_device = control.init().await; - //control.join_open("MikroTik-951589").await; - control.join_wpa2("MikroTik-951589", "fasdfasdfasdf").await; + control.join_open("MikroTik-951589").await; + //control.join_wpa2("MikroTik-951589", "asdfasdfasdfasdf").await; + + let config = embassy_net::ConfigStrategy::Dhcp; + //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), + //}); + + // Generate random seed + let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. + + // Init network stack + let stack = &*forever!(Stack::new( + net_device, + config, + forever!(StackResources::<1, 2, 8>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + // And now we can use it! + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + 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()); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {:02x}", &buf[..n]); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } } diff --git a/src/lib.rs b/src/lib.rs index e0c2c931..dde9d9c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,12 +12,19 @@ mod structs; use core::cell::Cell; use core::slice; +use core::sync::atomic::Ordering; +use core::task::Waker; +use atomic_polyfill::AtomicBool; +use embassy::blocking_mutex::raw::NoopRawMutex; +use embassy::channel::mpmc::Channel; use embassy::time::{block_for, Duration, Timer}; use embassy::util::yield_now; +use embassy_net::{PacketBoxExt, PacketBuf}; use embassy_rp::gpio::{Flex, Output, Pin}; use self::structs::*; +use crate::events::Event; fn swap16(x: u32) -> u32 { (x & 0xFF00FF00) >> 8 | (x & 0x00FF00FF) << 8 @@ -197,6 +204,10 @@ enum IoctlState { pub struct State { ioctl_id: Cell, ioctl_state: Cell, + + tx_channel: Channel, + rx_channel: Channel, + link_up: AtomicBool, } impl State { @@ -204,6 +215,10 @@ impl State { Self { ioctl_id: Cell::new(0), ioctl_state: Cell::new(IoctlState::Idle), + + tx_channel: Channel::new(), + rx_channel: Channel::new(), + link_up: AtomicBool::new(true), // TODO set up/down as we join/deassociate } } } @@ -213,7 +228,7 @@ pub struct Control<'a> { } impl<'a> Control<'a> { - pub async fn init(&mut self) { + pub async fn init(&mut self) -> NetDevice<'a> { const CHUNK_SIZE: usize = 1024; let clm = unsafe { slice::from_raw_parts(0x10140000 as *const u8, 4752) }; @@ -253,6 +268,15 @@ impl<'a> Control<'a> { self.set_iovar_u32("apsta", 1).await; //self.set_iovar("cur_etheraddr", &[02, 03, 04, 05, 06, 07]).await; + // read MAC addr. + let mut mac_addr = [0; 6]; + assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); + info!("mac addr: {:02x}", mac_addr); + + // TODO get_iovar is broken, it returns all zeros. + // Harcdode our own MAC for now. + let mac_addr = [0x28, 0xCD, 0xC1, 0x00, 0x3F, 0x05]; + let country = countries::WORLD_WIDE_XX; let country_info = CountryInfo { country_abbrev: [country.code[0], country.code[1], 0, 0], @@ -283,6 +307,15 @@ impl<'a> Control<'a> { iface: 0, events: [0xFF; 24], }; + + // Disable spammy uninteresting events. + evts.unset(Event::RADIO); + evts.unset(Event::IF); + evts.unset(Event::PROBREQ_MSG); + evts.unset(Event::PROBREQ_MSG_RX); + evts.unset(Event::PROBRESP_MSG); + evts.unset(Event::PROBRESP_MSG); + self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; Timer::after(Duration::from_millis(100)).await; @@ -305,6 +338,11 @@ impl<'a> Control<'a> { Timer::after(Duration::from_millis(100)).await; info!("INIT DONE"); + + NetDevice { + state: self.state, + mac_addr, + } } pub async fn join_open(&mut self, ssid: &str) { @@ -387,6 +425,7 @@ impl<'a> Control<'a> { self.ioctl(2, 263, 0, &mut buf).await; } + // TODO this is not really working, it always returns all zeros. async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { info!("get {}", name); @@ -395,7 +434,7 @@ impl<'a> Control<'a> { buf[name.len()] = 0; let total_len = name.len() + 1 + res.len(); - let res_len = self.ioctl(2, 262, 0, &mut buf[..total_len]).await - name.len() - 1; + let res_len = self.ioctl(0, 262, 0, &mut buf[..total_len]).await - name.len() - 1; res[..res_len].copy_from_slice(&buf[name.len() + 1..][..res_len]); res_len } @@ -408,9 +447,6 @@ impl<'a> Control<'a> { async fn ioctl(&mut self, kind: u32, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { // TODO cancel ioctl on future drop. - // snail mode 🐌 - Timer::after(Duration::from_millis(100)).await; - while !matches!(self.state.ioctl_state.get(), IoctlState::Idle) { yield_now().await; } @@ -434,6 +470,50 @@ impl<'a> Control<'a> { } } +pub struct NetDevice<'a> { + state: &'a State, + mac_addr: [u8; 6], +} + +impl<'a> embassy_net::Device for NetDevice<'a> { + fn register_waker(&mut self, waker: &Waker) { + // loopy loopy wakey wakey + waker.wake_by_ref() + } + + fn link_state(&mut self) -> embassy_net::LinkState { + match self.state.link_up.load(Ordering::Relaxed) { + true => embassy_net::LinkState::Up, + false => embassy_net::LinkState::Down, + } + } + + fn capabilities(&self) -> embassy_net::DeviceCapabilities { + let mut caps = embassy_net::DeviceCapabilities::default(); + caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header + caps.medium = embassy_net::Medium::Ethernet; + caps + } + + fn is_transmit_ready(&mut self) -> bool { + true + } + + fn transmit(&mut self, pkt: PacketBuf) { + if self.state.tx_channel.try_send(pkt).is_err() { + warn!("TX failed") + } + } + + fn receive(&mut self) -> Option { + self.state.rx_channel.try_recv().ok() + } + + fn ethernet_address(&self) -> [u8; 6] { + self.mac_addr + } +} + pub struct Runner<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> { state: &'a State, @@ -576,7 +656,10 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { while self.read32(FUNC_BUS, REG_BUS_STATUS) & STATUS_F2_RX_READY == 0 {} // Some random configs related to sleep. - // I think they're not needed if we don't want sleep...??? + // These aren't needed if we don't want to sleep the bus. + // TODO do we need to sleep the bus to read the irq line, due to + // being on the same pin as MOSI/MISO? + /* let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL); val |= 0x02; // WAKE_TILL_HT_AVAIL @@ -606,11 +689,16 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let mut buf = [0; 2048]; loop { // Send stuff + // TODO flow control if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() { self.send_ioctl(kind, cmd, iface, unsafe { &*buf }, self.state.ioctl_id.get()); self.state.ioctl_state.set(IoctlState::Sent { buf }); } + if let Ok(p) = self.state.tx_channel.try_recv() { + self.send_packet(&p); + } + // Receive stuff let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT); @@ -646,6 +734,52 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } } + fn send_packet(&mut self, packet: &[u8]) { + trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); + + let mut buf = [0; 2048]; + + let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); + + let seq = self.ioctl_seq; + self.ioctl_seq = self.ioctl_seq.wrapping_add(1); + + let sdpcm_header = SdpcmHeader { + len: total_len as u16, // TODO does this len need to be rounded up to u32? + len_inv: !total_len as u16, + sequence: seq, + channel_and_flags: 2, // data channel + next_length: 0, + header_length: SdpcmHeader::SIZE as _, + wireless_flow_control: 0, + bus_data_credit: 0, + reserved: [0, 0], + }; + + let bcd_header = BcdHeader { + flags: 0x20, + priority: 0, + flags2: 0, + data_offset: 0, + }; + trace!("tx {:?}", sdpcm_header); + trace!(" {:?}", bcd_header); + + buf[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); + buf[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); + buf[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); + + let total_len = (total_len + 3) & !3; // round up to 4byte + + trace!(" {:02x}", &buf[..total_len.min(48)]); + + let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); + self.cs.set_low(); + self.spi_write(&cmd.to_le_bytes()); + self.spi_write(&buf[..total_len]); + self.cs.set_high(); + } + fn rx(&mut self, packet: &[u8]) { if packet.len() < SdpcmHeader::SIZE { warn!("packet too short, len={}", packet.len()); @@ -683,6 +817,8 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { assert_eq!(cdc_header.status, 0); // todo propagate error instead let resp_len = cdc_header.len as usize; + info!("IOCTL Response: {:02x}", &payload[CdcHeader::SIZE..][..resp_len]); + (unsafe { &mut *buf }[..resp_len]).copy_from_slice(&payload[CdcHeader::SIZE..][..resp_len]); self.state.ioctl_state.set(IoctlState::Done { resp_len }); } @@ -703,14 +839,32 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let mut evt = EventHeader::from_bytes(&packet[24..][..EventHeader::SIZE].try_into().unwrap()); evt.byteswap(); let evt_data = &packet[24 + EventHeader::SIZE..][..evt.datalen as usize]; - info!( + debug!( "=== EVENT {}: {} {:02x}", events::Event::from(evt.event_type as u8), evt, evt_data ); } + 2 => { + let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); + trace!(" {:?}", bcd_header); + let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; + if packet_start > payload.len() { + warn!("packet start out of range."); + return; + } + let packet = &payload[packet_start..]; + trace!("rx pkt {:02x}", &packet[..(packet.len() as usize).min(48)]); + + let mut p = unwrap!(embassy_net::PacketBox::new(embassy_net::Packet::new())); + p[..packet.len()].copy_from_slice(packet); + + if let Err(_) = self.state.rx_channel.try_send(p.slice(0..packet.len())) { + warn!("failed to push rxd packet to the channel.") + } + } _ => {} } } diff --git a/src/structs.rs b/src/structs.rs index dd2c0cfe..060c2b06 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,3 +1,5 @@ +use crate::events::Event; + macro_rules! impl_bytes { ($t:ident) => { impl $t { @@ -157,3 +159,10 @@ pub struct EventMask { pub events: [u8; 24], } impl_bytes!(EventMask); + +impl EventMask { + pub fn unset(&mut self, evt: Event) { + let evt = evt as u8 as usize; + self.events[evt / 8] &= !(1 << (evt % 8)); + } +}