From ccf68d73910a68dc9e33beb666b20aba7430e015 Mon Sep 17 00:00:00 2001 From: elagil Date: Thu, 5 Sep 2024 21:29:04 +0200 Subject: feat(usb): add support for ISO endpoints --- embassy-stm32/src/usb/usb.rs | 211 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 186 insertions(+), 25 deletions(-) diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 9384c8688..0ab2306c8 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -80,6 +80,8 @@ impl interrupt::typelevel::Handler for InterruptHandl if istr.ctr() { let index = istr.ep_id() as usize; + CTR_TRIGGERED[index].store(true, Ordering::Relaxed); + let mut epr = regs.epr(index).read(); if epr.ctr_rx() { if index == 0 && epr.setup() { @@ -120,6 +122,10 @@ const USBRAM_ALIGN: usize = 4; const NEW_AW: AtomicWaker = AtomicWaker::new(); static BUS_WAKER: AtomicWaker = NEW_AW; static EP0_SETUP: AtomicBool = AtomicBool::new(false); + +const NEW_CTR_TRIGGERED: AtomicBool = AtomicBool::new(false); +static CTR_TRIGGERED: [AtomicBool; EP_COUNT] = [NEW_CTR_TRIGGERED; EP_COUNT]; + static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; static IRQ_RESET: AtomicBool = AtomicBool::new(false); @@ -163,20 +169,37 @@ fn calc_out_len(len: u16) -> (u16, u16) { mod btable { use super::*; - pub(super) fn write_in(index: usize, addr: u16) { + pub(super) fn write_in_tx(index: usize, addr: u16) { USBRAM.mem(index * 4 + 0).write_value(addr); } - pub(super) fn write_in_len(index: usize, _addr: u16, len: u16) { + pub(super) fn write_in_rx(index: usize, addr: u16) { + USBRAM.mem(index * 4 + 2).write_value(addr); + } + + pub(super) fn write_in_len_rx(index: usize, _addr: u16, len: u16) { + USBRAM.mem(index * 4 + 3).write_value(len); + } + + pub(super) fn write_in_len_tx(index: usize, _addr: u16, len: u16) { USBRAM.mem(index * 4 + 1).write_value(len); } - pub(super) fn write_out(index: usize, addr: u16, max_len_bits: u16) { + pub(super) fn write_out_rx(index: usize, addr: u16, max_len_bits: u16) { USBRAM.mem(index * 4 + 2).write_value(addr); USBRAM.mem(index * 4 + 3).write_value(max_len_bits); } - pub(super) fn read_out_len(index: usize) -> u16 { + pub(super) fn write_out_tx(index: usize, addr: u16, max_len_bits: u16) { + USBRAM.mem(index * 4 + 0).write_value(addr); + USBRAM.mem(index * 4 + 1).write_value(max_len_bits); + } + + pub(super) fn read_out_len_tx(index: usize) -> u16 { + USBRAM.mem(index * 4 + 1).read() + } + + pub(super) fn read_out_len_rx(index: usize) -> u16 { USBRAM.mem(index * 4 + 3).read() } } @@ -184,19 +207,37 @@ mod btable { mod btable { use super::*; - pub(super) fn write_in(_index: usize, _addr: u16) {} + pub(super) fn write_in_tx(_index: usize, _addr: u16) {} - pub(super) fn write_in_len(index: usize, addr: u16, len: u16) { + pub(super) fn write_in_rx(_index: usize, _addr: u16) {} + + pub(super) fn write_in_len_tx(index: usize, addr: u16, len: u16) { USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16)); } - pub(super) fn write_out(index: usize, addr: u16, max_len_bits: u16) { + pub(super) fn write_in_len_rx(index: usize, addr: u16, len: u16) { + USBRAM + .mem(index * 2 + 1) + .write_value((addr as u32) | ((len as u32) << 16)); + } + + pub(super) fn write_out_tx(index: usize, addr: u16, max_len_bits: u16) { + USBRAM + .mem(index * 2) + .write_value((addr as u32) | ((max_len_bits as u32) << 16)); + } + + pub(super) fn write_out_rx(index: usize, addr: u16, max_len_bits: u16) { USBRAM .mem(index * 2 + 1) .write_value((addr as u32) | ((max_len_bits as u32) << 16)); } - pub(super) fn read_out_len(index: usize) -> u16 { + pub(super) fn read_out_len_tx(index: usize) -> u16 { + (USBRAM.mem(index * 2).read() >> 16) as u16 + } + + pub(super) fn read_out_len_rx(index: usize) -> u16 { (USBRAM.mem(index * 2 + 1).read() >> 16) as u16 } } @@ -327,6 +368,13 @@ impl<'d, T: Instance> Driver<'d, T> { return false; // reserved for control pipe } let used = ep.used_out || ep.used_in; + if used && (ep.ep_type == EndpointType::Isochronous || ep.ep_type == EndpointType::Bulk) { + // Isochronous and bulk endpoints are double-buffered. + // Their corresponding endpoint/channel registers are forced to be unidirectional. + // Do not reuse this index. + return false; + } + let used_dir = match D::dir() { Direction::Out => ep.used_out, Direction::In => ep.used_in, @@ -350,7 +398,11 @@ impl<'d, T: Instance> Driver<'d, T> { let addr = self.alloc_ep_mem(len); trace!(" len_bits = {:04x}", len_bits); - btable::write_out::(index, addr, len_bits); + btable::write_out_rx::(index, addr, len_bits); + + if ep_type == EndpointType::Isochronous { + btable::write_out_tx::(index, addr, len_bits); + } EndpointBuffer { addr, @@ -366,7 +418,11 @@ impl<'d, T: Instance> Driver<'d, T> { let addr = self.alloc_ep_mem(len); // ep_in_len is written when actually TXing packets. - btable::write_in::(index, addr); + btable::write_in_tx::(index, addr); + + if ep_type == EndpointType::Isochronous { + btable::write_in_rx::(index, addr); + } EndpointBuffer { addr, @@ -656,6 +712,18 @@ impl Dir for Out { } } +/// Selects the packet buffer. +/// +/// For double-buffered endpoints, both the `Rx` and `Tx` buffer from a channel are used for the same +/// direction of transfer. This is opposed to single-buffered endpoints, where one channel can serve +/// two directions at the same time. +enum PacketBuffer { + /// The RX buffer - must be used for single-buffered OUT endpoints + Rx, + /// The TX buffer - must be used for single-buffered IN endpoints + Tx, +} + /// USB endpoint. pub struct Endpoint<'d, T: Instance, D> { _phantom: PhantomData<(&'d mut T, D)>, @@ -664,15 +732,46 @@ pub struct Endpoint<'d, T: Instance, D> { } impl<'d, T: Instance, D> Endpoint<'d, T, D> { - fn write_data(&mut self, buf: &[u8]) { + /// Write to a double-buffered endpoint. + /// + /// For double-buffered endpoints, the data buffers overlap, but we still need to write to the right counter field. + /// The DTOG_TX bit indicates the buffer that is currently in use by the USB peripheral, that is, the buffer in + /// which the next transmit packet will be stored, so we need to write the counter of the OTHER buffer, which is + /// where the last transmitted packet was stored. + fn write_data_double_buffered(&mut self, buf: &[u8], packet_buffer: PacketBuffer) { let index = self.info.addr.index(); self.buf.write(buf); - btable::write_in_len::(index, self.buf.addr, buf.len() as _); + + match packet_buffer { + PacketBuffer::Rx => btable::write_in_len_rx::(index, self.buf.addr, buf.len() as _), + PacketBuffer::Tx => btable::write_in_len_tx::(index, self.buf.addr, buf.len() as _), + } } - fn read_data(&mut self, buf: &mut [u8]) -> Result { + /// Write to a single-buffered endpoint. + fn write_data(&mut self, buf: &[u8]) { + self.write_data_double_buffered(buf, PacketBuffer::Tx); + } + + /// Read from a double-buffered endpoint. + /// + /// For double-buffered endpoints, the data buffers overlap, but we still need to read from the right counter field. + /// The DTOG_RX bit indicates the buffer that is currently in use by the USB peripheral, that is, the buffer in + /// which the next received packet will be stored, so we need to read the counter of the OTHER buffer, which is + /// where the last received packet was stored. + fn read_data_double_buffered( + &mut self, + buf: &mut [u8], + packet_buffer: PacketBuffer, + ) -> Result { let index = self.info.addr.index(); - let rx_len = btable::read_out_len::(index) as usize & 0x3FF; + + let rx_len = match packet_buffer { + PacketBuffer::Rx => btable::read_out_len_rx::(index), + PacketBuffer::Tx => btable::read_out_len_tx::(index), + } as usize + & 0x3FF; + trace!("READ DONE, rx_len = {}", rx_len); if rx_len > buf.len() { return Err(EndpointError::BufferOverflow); @@ -680,6 +779,11 @@ impl<'d, T: Instance, D> Endpoint<'d, T, D> { self.buf.read(&mut buf[..rx_len]); Ok(rx_len) } + + /// Read from a single-buffered endpoint. + fn read_data(&mut self, buf: &mut [u8]) -> Result { + self.read_data_double_buffered(buf, PacketBuffer::Rx) + } } impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { @@ -734,25 +838,53 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { EP_OUT_WAKERS[index].register(cx.waker()); let regs = T::regs(); let stat = regs.epr(index).read().stat_rx(); - if matches!(stat, Stat::NAK | Stat::DISABLED) { - Poll::Ready(stat) + if self.info.ep_type == EndpointType::Isochronous { + // The isochronous endpoint does not change its `STAT_RX` field to `NAK` when receiving a packet. + // Therefore, this instead waits until the `CTR` interrupt was triggered. + if matches!(stat, Stat::DISABLED) || CTR_TRIGGERED[index].load(Ordering::Relaxed) { + Poll::Ready(stat) + } else { + Poll::Pending + } } else { - Poll::Pending + if matches!(stat, Stat::NAK | Stat::DISABLED) { + Poll::Ready(stat) + } else { + Poll::Pending + } } }) .await; + CTR_TRIGGERED[index].store(false, Ordering::Relaxed); + if stat == Stat::DISABLED { return Err(EndpointError::Disabled); } - let rx_len = self.read_data(buf)?; - let regs = T::regs(); + + let packet_buffer = if self.info.ep_type == EndpointType::Isochronous { + // Find the buffer, which is currently in use. Read from the OTHER buffer. + if regs.epr(index).read().dtog_rx() { + PacketBuffer::Rx + } else { + PacketBuffer::Tx + } + } else { + PacketBuffer::Rx + }; + + let rx_len = self.read_data_double_buffered(buf, packet_buffer)?; + regs.epr(index).write(|w| { w.set_ep_type(convert_type(self.info.ep_type)); w.set_ea(self.info.addr.index() as _); - w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); + if self.info.ep_type == EndpointType::Isochronous { + w.set_stat_rx(Stat::from_bits(0)); // STAT_RX remains `VALID`. + } else { + w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); + } w.set_stat_tx(Stat::from_bits(0)); w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear @@ -776,25 +908,54 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { EP_IN_WAKERS[index].register(cx.waker()); let regs = T::regs(); let stat = regs.epr(index).read().stat_tx(); - if matches!(stat, Stat::NAK | Stat::DISABLED) { - Poll::Ready(stat) + if self.info.ep_type == EndpointType::Isochronous { + // The isochronous endpoint does not change its `STAT_RX` field to `NAK` when receiving a packet. + // Therefore, this instead waits until the `CTR` interrupt was triggered. + if matches!(stat, Stat::DISABLED) || CTR_TRIGGERED[index].load(Ordering::Relaxed) { + Poll::Ready(stat) + } else { + Poll::Pending + } } else { - Poll::Pending + if matches!(stat, Stat::NAK | Stat::DISABLED) { + Poll::Ready(stat) + } else { + Poll::Pending + } } }) .await; + CTR_TRIGGERED[index].store(false, Ordering::Relaxed); + if stat == Stat::DISABLED { return Err(EndpointError::Disabled); } - self.write_data(buf); + let regs = T::regs(); + + let packet_buffer = if self.info.ep_type == EndpointType::Isochronous { + // Find the buffer, which is currently in use. Write to the OTHER buffer. + if regs.epr(index).read().dtog_tx() { + PacketBuffer::Tx + } else { + PacketBuffer::Rx + } + } else { + PacketBuffer::Tx + }; + + self.write_data_double_buffered(buf, packet_buffer); let regs = T::regs(); regs.epr(index).write(|w| { w.set_ep_type(convert_type(self.info.ep_type)); w.set_ea(self.info.addr.index() as _); - w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); + if self.info.ep_type == EndpointType::Isochronous { + w.set_stat_tx(Stat::from_bits(0)); // STAT_TX remains `VALID`. + } else { + w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); + } w.set_stat_rx(Stat::from_bits(0)); w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear -- cgit From d37c482e2179c95740bb4284f4df30637551199b Mon Sep 17 00:00:00 2001 From: elagil Date: Thu, 5 Sep 2024 21:29:11 +0200 Subject: feat(usb-otg): add support for ISO endpoints --- embassy-usb-synopsys-otg/src/lib.rs | 30 ++++++++++++++++++++++++++++++ embassy-usb-synopsys-otg/src/otg_v1.rs | 16 ++++++++-------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index 5fb82db42..b145f4aa8 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs @@ -1071,6 +1071,21 @@ impl<'d> embassy_usb_driver::EndpointOut for Endpoint<'d, Out> { w.set_pktcnt(1); }); + if self.info.ep_type == EndpointType::Isochronous { + // Isochronous endpoints must set the correct even/odd frame bit to + // correspond with the next frame's number. + let frame_number = self.regs.dsts().read().fnsof(); + let frame_is_odd = frame_number & 0x01 == 1; + + self.regs.doepctl(index).modify(|r| { + if frame_is_odd { + r.set_sd0pid_sevnfrm(true); + } else { + r.set_sd1pid_soddfrm(true); + } + }); + } + // Clear NAK to indicate we are ready to receive more data self.regs.doepctl(index).modify(|w| { w.set_cnak(true); @@ -1158,6 +1173,21 @@ impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> { w.set_xfrsiz(buf.len() as _); }); + if self.info.ep_type == EndpointType::Isochronous { + // Isochronous endpoints must set the correct even/odd frame bit to + // correspond with the next frame's number. + let frame_number = self.regs.dsts().read().fnsof(); + let frame_is_odd = frame_number & 0x01 == 1; + + self.regs.diepctl(index).modify(|r| { + if frame_is_odd { + r.set_sd0pid_sevnfrm(true); + } else { + r.set_sd1pid_soddfrm(true); + } + }); + } + // Enable endpoint self.regs.diepctl(index).modify(|w| { w.set_cnak(true); diff --git a/embassy-usb-synopsys-otg/src/otg_v1.rs b/embassy-usb-synopsys-otg/src/otg_v1.rs index a8530980c..d3abc328d 100644 --- a/embassy-usb-synopsys-otg/src/otg_v1.rs +++ b/embassy-usb-synopsys-otg/src/otg_v1.rs @@ -795,15 +795,15 @@ pub mod regs { pub fn set_sd0pid_sevnfrm(&mut self, val: bool) { self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize); } - #[doc = "SODDFRM/SD1PID"] + #[doc = "SD1PID/SODDFRM"] #[inline(always)] - pub const fn soddfrm_sd1pid(&self) -> bool { + pub const fn sd1pid_soddfrm(&self) -> bool { let val = (self.0 >> 29usize) & 0x01; val != 0 } - #[doc = "SODDFRM/SD1PID"] + #[doc = "SD1PID/SODDFRM"] #[inline(always)] - pub fn set_soddfrm_sd1pid(&mut self, val: bool) { + pub fn set_sd1pid_soddfrm(&mut self, val: bool) { self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize); } #[doc = "EPDIS"] @@ -1174,15 +1174,15 @@ pub mod regs { pub fn set_sd0pid_sevnfrm(&mut self, val: bool) { self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize); } - #[doc = "SODDFRM"] + #[doc = "SD1PID/SODDFRM"] #[inline(always)] - pub const fn soddfrm(&self) -> bool { + pub const fn sd1pid_soddfrm(&self) -> bool { let val = (self.0 >> 29usize) & 0x01; val != 0 } - #[doc = "SODDFRM"] + #[doc = "SD1PID/SODDFRM"] #[inline(always)] - pub fn set_soddfrm(&mut self, val: bool) { + pub fn set_sd1pid_soddfrm(&mut self, val: bool) { self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize); } #[doc = "EPDIS"] -- cgit From a8ca6713e6e9b7ad5dd53f9b46bcf5e893adda1e Mon Sep 17 00:00:00 2001 From: elagil Date: Thu, 5 Sep 2024 21:29:24 +0200 Subject: feat(usb): make use of ISO endpoint support --- embassy-usb/src/builder.rs | 140 +++++++++++++++++++++++++++++++++++++----- embassy-usb/src/descriptor.rs | 91 ++++++++++++++++++++++++--- 2 files changed, 206 insertions(+), 25 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 7168e077c..e1bf8041f 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -1,8 +1,8 @@ use heapless::Vec; use crate::config::MAX_HANDLER_COUNT; -use crate::descriptor::{BosWriter, DescriptorWriter}; -use crate::driver::{Driver, Endpoint, EndpointType}; +use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType}; +use crate::driver::{Driver, Endpoint, EndpointInfo, EndpointType}; use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::types::{InterfaceNumber, StringIndex}; use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; @@ -414,7 +414,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { /// Descriptors are written in the order builder functions are called. Note that some /// classes care about the order. pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) { - self.builder.config_descriptor.write(descriptor_type, descriptor); + self.builder.config_descriptor.write(descriptor_type, descriptor, &[]); } /// Add a custom Binary Object Store (BOS) descriptor to this alternate setting. @@ -422,26 +422,80 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { self.builder.bos_descriptor.capability(capability_type, capability); } - fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { + /// Write a custom endpoint descriptor for a certain endpoint. + /// + /// This can be necessary, if the endpoint descriptors can only be written + /// after the endpoint was created. As an example, an endpoint descriptor + /// may contain the address of an endpoint that was allocated earlier. + pub fn endpoint_descriptor( + &mut self, + endpoint: &EndpointInfo, + synchronization_type: SynchronizationType, + usage_type: UsageType, + extra_fields: &[u8], + ) { + self.builder + .config_descriptor + .endpoint(endpoint, synchronization_type, usage_type, extra_fields); + } + + /// Allocate an IN endpoint, without writing its descriptor. + /// + /// Used for granular control over the order of endpoint and descriptor creation. + pub fn alloc_endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { let ep = self .builder .driver .alloc_endpoint_in(ep_type, max_packet_size, interval_ms) .expect("alloc_endpoint_in failed"); - self.builder.config_descriptor.endpoint(ep.info()); + ep + } + + fn endpoint_in( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + synchronization_type: SynchronizationType, + usage_type: UsageType, + extra_fields: &[u8], + ) -> D::EndpointIn { + let ep = self.alloc_endpoint_in(ep_type, max_packet_size, interval_ms); + self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); ep } - fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { + /// Allocate an OUT endpoint, without writing its descriptor. + /// + /// Use for granular control over the order of endpoint and descriptor creation. + pub fn alloc_endpoint_out( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + ) -> D::EndpointOut { let ep = self .builder .driver .alloc_endpoint_out(ep_type, max_packet_size, interval_ms) .expect("alloc_endpoint_out failed"); - self.builder.config_descriptor.endpoint(ep.info()); + ep + } + + fn endpoint_out( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + synchronization_type: SynchronizationType, + usage_type: UsageType, + extra_fields: &[u8], + ) -> D::EndpointOut { + let ep = self.alloc_endpoint_out(ep_type, max_packet_size, interval_ms); + self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); ep } @@ -451,7 +505,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { /// Descriptors are written in the order builder functions are called. Note that some /// classes care about the order. pub fn endpoint_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn { - self.endpoint_in(EndpointType::Bulk, max_packet_size, 0) + self.endpoint_in( + EndpointType::Bulk, + max_packet_size, + 0, + SynchronizationType::NoSynchronization, + UsageType::DataEndpoint, + &[], + ) } /// Allocate a BULK OUT endpoint and write its descriptor. @@ -459,7 +520,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { /// Descriptors are written in the order builder functions are called. Note that some /// classes care about the order. pub fn endpoint_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut { - self.endpoint_out(EndpointType::Bulk, max_packet_size, 0) + self.endpoint_out( + EndpointType::Bulk, + max_packet_size, + 0, + SynchronizationType::NoSynchronization, + UsageType::DataEndpoint, + &[], + ) } /// Allocate a INTERRUPT IN endpoint and write its descriptor. @@ -467,24 +535,66 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { /// Descriptors are written in the order builder functions are called. Note that some /// classes care about the order. pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { - self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval_ms) + self.endpoint_in( + EndpointType::Interrupt, + max_packet_size, + interval_ms, + SynchronizationType::NoSynchronization, + UsageType::DataEndpoint, + &[], + ) } /// Allocate a INTERRUPT OUT endpoint and write its descriptor. pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { - self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval_ms) + self.endpoint_out( + EndpointType::Interrupt, + max_packet_size, + interval_ms, + SynchronizationType::NoSynchronization, + UsageType::DataEndpoint, + &[], + ) } /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor. /// /// Descriptors are written in the order builder functions are called. Note that some /// classes care about the order. - pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { - self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms) + pub fn endpoint_isochronous_in( + &mut self, + max_packet_size: u16, + interval_ms: u8, + synchronization_type: SynchronizationType, + usage_type: UsageType, + extra_fields: &[u8], + ) -> D::EndpointIn { + self.endpoint_in( + EndpointType::Isochronous, + max_packet_size, + interval_ms, + synchronization_type, + usage_type, + extra_fields, + ) } /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. - pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { - self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms) + pub fn endpoint_isochronous_out( + &mut self, + max_packet_size: u16, + interval_ms: u8, + synchronization_type: SynchronizationType, + usage_type: UsageType, + extra_fields: &[u8], + ) -> D::EndpointOut { + self.endpoint_out( + EndpointType::Isochronous, + max_packet_size, + interval_ms, + synchronization_type, + usage_type, + extra_fields, + ) } } diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index f1773fa8a..0f2931c38 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -1,4 +1,5 @@ //! Utilities for writing USB descriptors. +use embassy_usb_driver::EndpointType; use crate::builder::Config; use crate::driver::EndpointInfo; @@ -38,6 +39,40 @@ pub mod capability_type { pub const PLATFORM: u8 = 5; } +/// USB endpoint synchronization type. The values of this enum can be directly +/// cast into `u8` to get the bmAttributes synchronization type bits. +/// Values other than `NoSynchronization` are only allowed on isochronous endpoints. +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SynchronizationType { + /// No synchronization is used. + NoSynchronization = 0b00, + /// Unsynchronized, although sinks provide data rate feedback. + Asynchronous = 0b01, + /// Synchronized using feedback or feedforward data rate information. + Adaptive = 0b10, + /// Synchronized to the USB’s SOF. + Synchronous = 0b11, +} + +/// USB endpoint usage type. The values of this enum can be directly cast into +/// `u8` to get the bmAttributes usage type bits. +/// Values other than `DataEndpoint` are only allowed on isochronous endpoints. +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum UsageType { + /// Use the endpoint for regular data transfer. + DataEndpoint = 0b00, + /// Endpoint conveys explicit feedback information for one or more data endpoints. + FeedbackEndpoint = 0b01, + /// A data endpoint that also serves as an implicit feedback endpoint for one or more data endpoints. + ImplicitFeedbackDataEndpoint = 0b10, + /// Reserved usage type. + Reserved = 0b11, +} + /// A writer for USB descriptors. pub(crate) struct DescriptorWriter<'a> { pub buf: &'a mut [u8], @@ -65,23 +100,26 @@ impl<'a> DescriptorWriter<'a> { self.position } - /// Writes an arbitrary (usually class-specific) descriptor. - pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) { - let length = descriptor.len(); + /// Writes an arbitrary (usually class-specific) descriptor with optional extra fields. + pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8], extra_fields: &[u8]) { + let descriptor_length = descriptor.len(); + let extra_fields_length = extra_fields.len(); + let total_length = descriptor_length + extra_fields_length; assert!( - (self.position + 2 + length) <= self.buf.len() && (length + 2) <= 255, + (self.position + 2 + total_length) <= self.buf.len() && (total_length + 2) <= 255, "Descriptor buffer full" ); - self.buf[self.position] = (length + 2) as u8; + self.buf[self.position] = (total_length + 2) as u8; self.buf[self.position + 1] = descriptor_type; let start = self.position + 2; - self.buf[start..start + length].copy_from_slice(descriptor); + self.buf[start..start + descriptor_length].copy_from_slice(descriptor); + self.buf[start + descriptor_length..start + total_length].copy_from_slice(extra_fields); - self.position = start + length; + self.position = start + total_length; } pub(crate) fn configuration(&mut self, config: &Config) { @@ -99,6 +137,7 @@ impl<'a> DescriptorWriter<'a> { | if config.supports_remote_wakeup { 0x20 } else { 0x00 }, // bmAttributes (config.max_power / 2) as u8, // bMaxPower ], + &[], ); } @@ -145,6 +184,7 @@ impl<'a> DescriptorWriter<'a> { function_protocol, 0, ], + &[], ); } @@ -195,6 +235,7 @@ impl<'a> DescriptorWriter<'a> { interface_protocol, // bInterfaceProtocol str_index, // iInterface ], + &[], ); } @@ -204,21 +245,50 @@ impl<'a> DescriptorWriter<'a> { /// /// * `endpoint` - Endpoint previously allocated with /// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder). - pub fn endpoint(&mut self, endpoint: &EndpointInfo) { + /// * `synchronization_type` - The synchronization type of the endpoint. + /// * `usage_type` - The usage type of the endpoint. + /// * `extra_fields` - Additional, class-specific entries at the end of the endpoint descriptor. + pub fn endpoint( + &mut self, + endpoint: &EndpointInfo, + synchronization_type: SynchronizationType, + usage_type: UsageType, + extra_fields: &[u8], + ) { match self.num_endpoints_mark { Some(mark) => self.buf[mark] += 1, None => panic!("you can only call `endpoint` after `interface/interface_alt`."), }; + let mut bm_attributes = endpoint.ep_type as u8; + + // Synchronization types other than `NoSynchronization`, + // and usage types other than `DataEndpoint` + // are only allowed for isochronous endpoints. + if endpoint.ep_type != EndpointType::Isochronous { + assert_eq!(synchronization_type, SynchronizationType::NoSynchronization); + assert_eq!(usage_type, UsageType::DataEndpoint); + } else { + if usage_type == UsageType::FeedbackEndpoint { + assert_eq!(synchronization_type, SynchronizationType::NoSynchronization) + } + + let synchronization_bm_attibutes: u8 = (synchronization_type as u8) << 2; + let usage_bm_attibutes: u8 = (usage_type as u8) << 4; + + bm_attributes |= usage_bm_attibutes | synchronization_bm_attibutes; + } + self.write( descriptor_type::ENDPOINT, &[ - endpoint.addr.into(), // bEndpointAddress - endpoint.ep_type as u8, // bmAttributes + endpoint.addr.into(), // bEndpointAddress + bm_attributes, // bmAttributes endpoint.max_packet_size as u8, (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize endpoint.interval_ms, // bInterval ], + extra_fields, ); } @@ -315,6 +385,7 @@ impl<'a> BosWriter<'a> { 0x00, 0x00, // wTotalLength 0x00, // bNumDeviceCaps ], + &[], ); self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4]); -- cgit From 3d6a270f30c45eaf394c8eb8bf182dd1a7ec2d7b Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Wed, 11 Sep 2024 19:44:11 -0400 Subject: rp: Fix indexing for pins >31 on rp235xb (#3330) * Fix indexing for pins >31 on rp235xb * fixup knowing that 1<<7 is 128 not 64 --- embassy-rp/src/gpio.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index d0bb7e574..31397172c 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -846,12 +846,12 @@ pub(crate) trait SealedPin: Sized { #[inline] fn _pin(&self) -> u8 { - self.pin_bank() & 0x1f + self.pin_bank() & 0x7f } #[inline] fn _bank(&self) -> Bank { - match self.pin_bank() >> 5 { + match self.pin_bank() >> 7 { #[cfg(feature = "qspi-as-gpio")] 1 => Bank::Qspi, _ => Bank::Bank0, @@ -880,15 +880,27 @@ pub(crate) trait SealedPin: Sized { } fn sio_out(&self) -> pac::sio::Gpio { - SIO.gpio_out(self._bank() as _) + if cfg!(feature = "rp2040") { + SIO.gpio_out(self._bank() as _) + } else { + SIO.gpio_out((self._pin() / 32) as _) + } } fn sio_oe(&self) -> pac::sio::Gpio { - SIO.gpio_oe(self._bank() as _) + if cfg!(feature = "rp2040") { + SIO.gpio_oe(self._bank() as _) + } else { + SIO.gpio_oe((self._pin() / 32) as _) + } } fn sio_in(&self) -> Reg { - SIO.gpio_in(self._bank() as _) + if cfg!(feature = "rp2040") { + SIO.gpio_in(self._bank() as _) + } else { + SIO.gpio_in((self._pin() / 32) as _) + } } fn int_proc(&self) -> pac::io::Int { @@ -953,7 +965,7 @@ macro_rules! impl_pin { impl SealedPin for peripherals::$name { #[inline] fn pin_bank(&self) -> u8 { - ($bank as u8) * 32 + $pin_num + ($bank as u8) * 128 + $pin_num } } -- cgit From 8519e54461b8d3c3c2a97c88823498c878187ba4 Mon Sep 17 00:00:00 2001 From: Olivier Hériveaux Date: Mon, 16 Sep 2024 11:48:54 +0200 Subject: fix(boot): return signature error when no features Always return signature error in verify_and_mark_updated when no signature features are enabled. --- embassy-boot/src/firmware_updater/asynch.rs | 12 ++++++++---- embassy-boot/src/firmware_updater/blocking.rs | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index 26f65f295..86b441592 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs @@ -107,7 +107,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { let mut message = [0; 64]; self.hash::(_update_len, &mut chunk_buf, &mut message).await?; - public_key.verify(&message, &signature).map_err(into_signature_error)? + public_key.verify(&message, &signature).map_err(into_signature_error)?; + return self.state.mark_updated().await; } #[cfg(feature = "ed25519-salty")] { @@ -134,10 +135,13 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { message, r.is_ok() ); - r.map_err(into_signature_error)? + r.map_err(into_signature_error)?; + return self.state.mark_updated().await; + } + #[cfg(not(any(feature = "ed25519-dalek", feature = "ed25519-salty")))] + { + Err(FirmwareUpdaterError::Signature(signature::Error::new())) } - - self.state.mark_updated().await } /// Verify the update in DFU with any digest. diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index 35772a856..d3c723456 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -142,7 +142,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> let mut chunk_buf = [0; 2]; self.hash::(_update_len, &mut chunk_buf, &mut message)?; - public_key.verify(&message, &signature).map_err(into_signature_error)? + public_key.verify(&message, &signature).map_err(into_signature_error)?; + return self.state.mark_updated(); } #[cfg(feature = "ed25519-salty")] { @@ -169,10 +170,13 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> message, r.is_ok() ); - r.map_err(into_signature_error)? + r.map_err(into_signature_error)?; + return self.state.mark_updated(); + } + #[cfg(not(any(feature = "ed25519-dalek", feature = "ed25519-salty")))] + { + Err(FirmwareUpdaterError::Signature(signature::Error::new())) } - - self.state.mark_updated() } /// Verify the update in DFU with any digest. -- cgit From 313e76af043c8b2bb2b31f8cc0eba7578ea41357 Mon Sep 17 00:00:00 2001 From: Sebastian Quilitz Date: Mon, 16 Sep 2024 12:26:00 +0200 Subject: rp: add constructor for tx-only blocking UART --- embassy-rp/src/uart/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index aba4b792a..c94e5e185 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -224,6 +224,17 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { } impl<'d, T: Instance> UartTx<'d, T, Blocking> { + /// Create a new UART TX instance for blocking mode operations. + pub fn new_blocking( + _uart: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + into_ref!(tx); + Uart::::init(Some(tx.map_into()), None, None, None, config); + Self::new_inner(None) + } + /// Convert this uart TX instance into a buffered uart using the provided /// irq and transmit buffer. pub fn into_buffered( -- cgit From be0d9775e3bcc3c1bd1448e357d7c6cd67b68991 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 11 Sep 2024 22:06:26 +0200 Subject: net: refactor to simplify lifetimes/generics. --- ci.sh | 2 +- embassy-net/Cargo.toml | 10 +- embassy-net/README.md | 3 +- embassy-net/src/device.rs | 106 ----- embassy-net/src/dns.rs | 25 +- embassy-net/src/driver_util.rs | 106 +++++ embassy-net/src/lib.rs | 445 +++++++++------------ embassy-net/src/raw.rs | 53 +-- embassy-net/src/tcp.rs | 76 ++-- embassy-net/src/udp.rs | 63 +-- examples/nrf52840/src/bin/ethernet_enc28j60.rs | 14 +- examples/nrf52840/src/bin/usb_ethernet.rs | 11 +- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 11 +- examples/nrf9160/src/bin/modem_tcp_client.rs | 16 +- examples/rp/src/bin/ethernet_w5500_multisocket.rs | 19 +- examples/rp/src/bin/ethernet_w5500_tcp_client.rs | 13 +- examples/rp/src/bin/ethernet_w5500_tcp_server.rs | 13 +- examples/rp/src/bin/ethernet_w5500_udp.rs | 13 +- examples/rp/src/bin/usb_ethernet.rs | 11 +- examples/rp/src/bin/wifi_ap_tcp_server.rs | 18 +- examples/rp/src/bin/wifi_scan.rs | 5 +- examples/rp/src/bin/wifi_tcp_server.rs | 18 +- examples/rp/src/bin/wifi_webrequest.rs | 18 +- examples/std/src/bin/net.rs | 11 +- examples/std/src/bin/net_dns.rs | 11 +- examples/std/src/bin/net_ppp.rs | 17 +- examples/std/src/bin/net_udp.rs | 11 +- examples/std/src/bin/tcp_accept.rs | 11 +- examples/stm32f4/src/bin/eth.rs | 13 +- examples/stm32f4/src/bin/eth_w5500.rs | 11 +- examples/stm32f4/src/bin/usb_ethernet.rs | 11 +- examples/stm32f7/src/bin/eth.rs | 13 +- examples/stm32h5/src/bin/eth.rs | 13 +- examples/stm32h7/src/bin/eth.rs | 13 +- examples/stm32h7/src/bin/eth_client.rs | 13 +- examples/stm32h7/src/bin/eth_client_mii.rs | 13 +- .../stm32l4/src/bin/spe_adin1110_http_server.rs | 11 +- examples/stm32l5/src/bin/usb_ethernet.rs | 11 +- tests/nrf/src/bin/ethernet_enc28j60_perf.rs | 11 +- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 13 +- tests/perf-client/src/lib.rs | 9 +- tests/rp/src/bin/cyw43-perf.rs | 13 +- tests/rp/src/bin/ethernet_w5100s_perf.rs | 13 +- tests/stm32/src/bin/eth.rs | 11 +- 44 files changed, 609 insertions(+), 713 deletions(-) delete mode 100644 embassy-net/src/device.rs create mode 100644 embassy-net/src/driver_util.rs diff --git a/ci.sh b/ci.sh index 8fef731a4..5e8bddb8c 100755 --- a/ci.sh +++ b/ci.sh @@ -47,7 +47,7 @@ cargo batch \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,igmp,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,dhcpv4-hostname \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 28bac485b..2e21b4231 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -16,11 +16,11 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["defmt", "tcp", "udp", "raw", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"] +features = ["defmt", "tcp", "udp", "raw", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "multicast", "dhcpv4-hostname"] target = "thumbv7em-none-eabi" [package.metadata.docs.rs] -features = ["defmt", "tcp", "udp", "raw", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"] +features = ["defmt", "tcp", "udp", "raw", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "multicast", "dhcpv4-hostname"] [features] default = [] @@ -60,15 +60,15 @@ medium-ethernet = ["smoltcp/medium-ethernet"] medium-ip = ["smoltcp/medium-ip"] ## Enable the IEEE 802.15.4 medium medium-ieee802154 = ["smoltcp/medium-ieee802154"] -## Enable IGMP support -igmp = ["smoltcp/proto-igmp"] +## Enable multicast support (for both ipv4 and/or ipv6 if enabled) +multicast = ["smoltcp/multicast"] [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -smoltcp = { version = "0.11.0", default-features = false, features = [ +smoltcp = { git="https://github.com/smoltcp-rs/smoltcp", rev="dd43c8f189178b0ab3bda798ed8578b5b0a6f094", default-features = false, features = [ "socket", "async", ] } diff --git a/embassy-net/README.md b/embassy-net/README.md index 6b7d46934..1722ffc7b 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -10,8 +10,9 @@ memory management designed to work well for embedded systems, aiming for a more - IPv4, IPv6 - Ethernet and bare-IP mediums. -- TCP, UDP, DNS, DHCPv4, IGMPv4 +- TCP, UDP, DNS, DHCPv4 - TCP sockets implement the `embedded-io` async traits. +- Multicast See the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and unimplemented features of the network protocols. diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs deleted file mode 100644 index 3b1d3c47c..000000000 --- a/embassy-net/src/device.rs +++ /dev/null @@ -1,106 +0,0 @@ -use core::task::Context; - -use embassy_net_driver::{Capabilities, Checksum, Driver, RxToken, TxToken}; -use smoltcp::phy::{self, Medium}; -use smoltcp::time::Instant; - -pub(crate) struct DriverAdapter<'d, 'c, T> -where - T: Driver, -{ - // must be Some when actually using this to rx/tx - pub cx: Option<&'d mut Context<'c>>, - pub inner: &'d mut T, - pub medium: Medium, -} - -impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T> -where - T: Driver, -{ - type RxToken<'a> = RxTokenAdapter> where Self: 'a; - type TxToken<'a> = TxTokenAdapter> where Self: 'a; - - fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - self.inner - .receive(unwrap!(self.cx.as_deref_mut())) - .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) - } - - /// Construct a transmit token. - fn transmit(&mut self, _timestamp: Instant) -> Option> { - self.inner.transmit(unwrap!(self.cx.as_deref_mut())).map(TxTokenAdapter) - } - - /// Get a description of device capabilities. - fn capabilities(&self) -> phy::DeviceCapabilities { - fn convert(c: Checksum) -> phy::Checksum { - match c { - Checksum::Both => phy::Checksum::Both, - Checksum::Tx => phy::Checksum::Tx, - Checksum::Rx => phy::Checksum::Rx, - Checksum::None => phy::Checksum::None, - } - } - let caps: Capabilities = self.inner.capabilities(); - let mut smolcaps = phy::DeviceCapabilities::default(); - - smolcaps.max_transmission_unit = caps.max_transmission_unit; - smolcaps.max_burst_size = caps.max_burst_size; - smolcaps.medium = self.medium; - smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4); - smolcaps.checksum.tcp = convert(caps.checksum.tcp); - smolcaps.checksum.udp = convert(caps.checksum.udp); - #[cfg(feature = "proto-ipv4")] - { - smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); - } - #[cfg(feature = "proto-ipv6")] - { - smolcaps.checksum.icmpv6 = convert(caps.checksum.icmpv6); - } - - smolcaps - } -} - -pub(crate) struct RxTokenAdapter(T) -where - T: RxToken; - -impl phy::RxToken for RxTokenAdapter -where - T: RxToken, -{ - fn consume(self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - self.0.consume(|buf| { - #[cfg(feature = "packet-trace")] - trace!("rx: {:?}", buf); - f(buf) - }) - } -} - -pub(crate) struct TxTokenAdapter(T) -where - T: TxToken; - -impl phy::TxToken for TxTokenAdapter -where - T: TxToken, -{ - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - self.0.consume(len, |buf| { - let r = f(buf); - #[cfg(feature = "packet-trace")] - trace!("tx: {:?}", buf); - r - }) - } -} diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 8ccfa4e4f..1fbaea4f0 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -9,7 +9,7 @@ pub use smoltcp::socket::dns::{DnsQuery, Socket}; pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; -use crate::{Driver, Stack}; +use crate::Stack; /// Errors returned by DnsSocket. #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -44,21 +44,15 @@ impl From for Error { /// This exists only for compatibility with crates that use `embedded-nal-async`. /// Prefer using [`Stack::dns_query`](crate::Stack::dns_query) directly if you're /// not using `embedded-nal-async`. -pub struct DnsSocket<'a, D> -where - D: Driver + 'static, -{ - stack: &'a Stack, +pub struct DnsSocket<'a> { + stack: Stack<'a>, } -impl<'a, D> DnsSocket<'a, D> -where - D: Driver + 'static, -{ +impl<'a> DnsSocket<'a> { /// Create a new DNS socket using the provided stack. /// /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated. - pub fn new(stack: &'a Stack) -> Self { + pub fn new(stack: Stack<'a>) -> Self { Self { stack } } @@ -72,10 +66,7 @@ where } } -impl<'a, D> embedded_nal_async::Dns for DnsSocket<'a, D> -where - D: Driver + 'static, -{ +impl<'a> embedded_nal_async::Dns for DnsSocket<'a> { type Error = Error; async fn get_host_by_name( @@ -124,3 +115,7 @@ where todo!() } } + +fn _assert_covariant<'a, 'b: 'a>(x: DnsSocket<'b>) -> DnsSocket<'a> { + x +} diff --git a/embassy-net/src/driver_util.rs b/embassy-net/src/driver_util.rs new file mode 100644 index 000000000..b2af1d499 --- /dev/null +++ b/embassy-net/src/driver_util.rs @@ -0,0 +1,106 @@ +use core::task::Context; + +use embassy_net_driver::{Capabilities, Checksum, Driver, RxToken, TxToken}; +use smoltcp::phy::{self, Medium}; +use smoltcp::time::Instant; + +pub(crate) struct DriverAdapter<'d, 'c, T> +where + T: Driver, +{ + // must be Some when actually using this to rx/tx + pub cx: Option<&'d mut Context<'c>>, + pub inner: &'d mut T, + pub medium: Medium, +} + +impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T> +where + T: Driver, +{ + type RxToken<'a> = RxTokenAdapter> where Self: 'a; + type TxToken<'a> = TxTokenAdapter> where Self: 'a; + + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + self.inner + .receive(unwrap!(self.cx.as_deref_mut())) + .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) + } + + /// Construct a transmit token. + fn transmit(&mut self, _timestamp: Instant) -> Option> { + self.inner.transmit(unwrap!(self.cx.as_deref_mut())).map(TxTokenAdapter) + } + + /// Get a description of device capabilities. + fn capabilities(&self) -> phy::DeviceCapabilities { + fn convert(c: Checksum) -> phy::Checksum { + match c { + Checksum::Both => phy::Checksum::Both, + Checksum::Tx => phy::Checksum::Tx, + Checksum::Rx => phy::Checksum::Rx, + Checksum::None => phy::Checksum::None, + } + } + let caps: Capabilities = self.inner.capabilities(); + let mut smolcaps = phy::DeviceCapabilities::default(); + + smolcaps.max_transmission_unit = caps.max_transmission_unit; + smolcaps.max_burst_size = caps.max_burst_size; + smolcaps.medium = self.medium; + smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4); + smolcaps.checksum.tcp = convert(caps.checksum.tcp); + smolcaps.checksum.udp = convert(caps.checksum.udp); + #[cfg(feature = "proto-ipv4")] + { + smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); + } + #[cfg(feature = "proto-ipv6")] + { + smolcaps.checksum.icmpv6 = convert(caps.checksum.icmpv6); + } + + smolcaps + } +} + +pub(crate) struct RxTokenAdapter(T) +where + T: RxToken; + +impl phy::RxToken for RxTokenAdapter +where + T: RxToken, +{ + fn consume(self, f: F) -> R + where + F: FnOnce(&[u8]) -> R, + { + self.0.consume(|buf| { + #[cfg(feature = "packet-trace")] + trace!("rx: {:?}", buf); + f(buf) + }) + } +} + +pub(crate) struct TxTokenAdapter(T) +where + T: TxToken; + +impl phy::TxToken for TxTokenAdapter +where + T: TxToken, +{ + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + self.0.consume(len, |buf| { + let r = f(buf); + #[cfg(feature = "packet-trace")] + trace!("tx: {:?}", buf); + r + }) + } +} diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 56321cec9..ef53fb905 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -12,9 +12,9 @@ compile_error!("You must enable at least one of the following features: proto-ip // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -mod device; #[cfg(feature = "dns")] pub mod dns; +mod driver_util; #[cfg(feature = "raw")] pub mod raw; #[cfg(feature = "tcp")] @@ -25,6 +25,7 @@ pub mod udp; use core::cell::RefCell; use core::future::{poll_fn, Future}; +use core::mem::MaybeUninit; use core::pin::pin; use core::task::{Context, Poll}; @@ -36,7 +37,7 @@ use embassy_time::{Instant, Timer}; use heapless::Vec; #[cfg(feature = "dns")] pub use smoltcp::config::DNS_MAX_SERVER_COUNT; -#[cfg(feature = "igmp")] +#[cfg(feature = "multicast")] pub use smoltcp::iface::MulticastError; #[allow(unused_imports)] use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; @@ -57,7 +58,7 @@ pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; -use crate::device::DriverAdapter; +use crate::driver_util::DriverAdapter; use crate::time::{instant_from_smoltcp, instant_to_smoltcp}; const LOCAL_PORT_MIN: u16 = 1025; @@ -69,33 +70,33 @@ const MAX_HOSTNAME_LEN: usize = 32; /// Memory resources needed for a network stack. pub struct StackResources { - sockets: [SocketStorage<'static>; SOCK], + sockets: MaybeUninit<[SocketStorage<'static>; SOCK]>, + inner: MaybeUninit>, #[cfg(feature = "dns")] - queries: [Option; MAX_QUERIES], + queries: MaybeUninit<[Option; MAX_QUERIES]>, #[cfg(feature = "dhcpv4-hostname")] - hostname: core::cell::UnsafeCell, + hostname: HostnameResources, } #[cfg(feature = "dhcpv4-hostname")] struct HostnameResources { - option: smoltcp::wire::DhcpOption<'static>, - data: [u8; MAX_HOSTNAME_LEN], + option: MaybeUninit>, + data: MaybeUninit<[u8; MAX_HOSTNAME_LEN]>, } impl StackResources { /// Create a new set of stack resources. pub const fn new() -> Self { - #[cfg(feature = "dns")] - const INIT: Option = None; Self { - sockets: [SocketStorage::EMPTY; SOCK], + sockets: MaybeUninit::uninit(), + inner: MaybeUninit::uninit(), #[cfg(feature = "dns")] - queries: [INIT; MAX_QUERIES], + queries: MaybeUninit::uninit(), #[cfg(feature = "dhcpv4-hostname")] - hostname: core::cell::UnsafeCell::new(HostnameResources { - option: smoltcp::wire::DhcpOption { kind: 0, data: &[] }, - data: [0; MAX_HOSTNAME_LEN], - }), + hostname: HostnameResources { + option: MaybeUninit::uninit(), + data: MaybeUninit::uninit(), + }, } } } @@ -239,16 +240,29 @@ pub enum ConfigV6 { Static(StaticConfigV6), } -/// A network stack. +/// Network stack runner. +/// +/// You must call [`Runner::run()`] in a background task for the network stack to work. +pub struct Runner<'d, D: Driver> { + driver: D, + stack: Stack<'d>, +} + +/// Network stack handle /// -/// This is the main entry point for the network stack. -pub struct Stack { - pub(crate) socket: RefCell, - inner: RefCell>, +/// Use this to create sockets. It's `Copy`, so you can pass +/// it by value instead of by reference. +#[derive(Copy, Clone)] +pub struct Stack<'d> { + inner: &'d RefCell, } -struct Inner { - device: D, +pub(crate) struct Inner { + pub(crate) sockets: SocketSet<'static>, // Lifetime type-erased. + pub(crate) iface: Interface, + pub(crate) waker: WakerRegistration, + hardware_address: HardwareAddress, + next_local_port: u16, link_up: bool, #[cfg(feature = "proto-ipv4")] static_v4: Option, @@ -262,14 +276,83 @@ struct Inner { #[cfg(feature = "dns")] dns_waker: WakerRegistration, #[cfg(feature = "dhcpv4-hostname")] - hostname: &'static mut core::cell::UnsafeCell, + hostname: *mut HostnameResources, } -pub(crate) struct SocketStack { - pub(crate) sockets: SocketSet<'static>, - pub(crate) iface: Interface, - pub(crate) waker: WakerRegistration, - next_local_port: u16, +fn _assert_covariant<'a, 'b: 'a>(x: Stack<'b>) -> Stack<'a> { + x +} + +/// Create a new network stack. +pub fn new<'d, D: Driver, const SOCK: usize>( + mut driver: D, + config: Config, + resources: &'d mut StackResources, + random_seed: u64, +) -> (Stack<'d>, Runner<'d, D>) { + let (hardware_address, medium) = to_smoltcp_hardware_address(driver.hardware_address()); + let mut iface_cfg = smoltcp::iface::Config::new(hardware_address); + iface_cfg.random_seed = random_seed; + + let iface = Interface::new( + iface_cfg, + &mut DriverAdapter { + inner: &mut driver, + cx: None, + medium, + }, + instant_to_smoltcp(Instant::now()), + ); + + unsafe fn transmute_slice(x: &mut [T]) -> &'static mut [T] { + core::mem::transmute(x) + } + + let sockets = resources.sockets.write([SocketStorage::EMPTY; SOCK]); + #[allow(unused_mut)] + let mut sockets: SocketSet<'static> = SocketSet::new(unsafe { transmute_slice(sockets) }); + + let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; + + #[cfg(feature = "dns")] + let dns_socket = sockets.add(dns::Socket::new( + &[], + managed::ManagedSlice::Borrowed(unsafe { + transmute_slice(resources.queries.write([const { None }; MAX_QUERIES])) + }), + )); + + let mut inner = Inner { + sockets, + iface, + waker: WakerRegistration::new(), + next_local_port, + hardware_address, + link_up: false, + #[cfg(feature = "proto-ipv4")] + static_v4: None, + #[cfg(feature = "proto-ipv6")] + static_v6: None, + #[cfg(feature = "dhcpv4")] + dhcp_socket: None, + config_waker: WakerRegistration::new(), + #[cfg(feature = "dns")] + dns_socket, + #[cfg(feature = "dns")] + dns_waker: WakerRegistration::new(), + #[cfg(feature = "dhcpv4-hostname")] + hostname: &mut resources.hostname, + }; + + #[cfg(feature = "proto-ipv4")] + inner.set_config_v4(config.ipv4); + #[cfg(feature = "proto-ipv6")] + inner.set_config_v6(config.ipv6); + inner.apply_static_config(); + + let inner = &*resources.inner.write(RefCell::new(inner)); + let stack = Stack { inner }; + (stack, Runner { driver, stack }) } fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddress, Medium) { @@ -292,89 +375,23 @@ fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddres } } -impl Stack { - /// Create a new network stack. - pub fn new( - mut device: D, - config: Config, - resources: &'static mut StackResources, - random_seed: u64, - ) -> Self { - let (hardware_addr, medium) = to_smoltcp_hardware_address(device.hardware_address()); - let mut iface_cfg = smoltcp::iface::Config::new(hardware_addr); - iface_cfg.random_seed = random_seed; - - let iface = Interface::new( - iface_cfg, - &mut DriverAdapter { - inner: &mut device, - cx: None, - medium, - }, - instant_to_smoltcp(Instant::now()), - ); - - let sockets = SocketSet::new(&mut resources.sockets[..]); - - let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; - - #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))] - let mut socket = SocketStack { - sockets, - iface, - waker: WakerRegistration::new(), - next_local_port, - }; - - let mut inner = Inner { - device, - link_up: false, - #[cfg(feature = "proto-ipv4")] - static_v4: None, - #[cfg(feature = "proto-ipv6")] - static_v6: None, - #[cfg(feature = "dhcpv4")] - dhcp_socket: None, - config_waker: WakerRegistration::new(), - #[cfg(feature = "dns")] - dns_socket: socket.sockets.add(dns::Socket::new( - &[], - managed::ManagedSlice::Borrowed(&mut resources.queries), - )), - #[cfg(feature = "dns")] - dns_waker: WakerRegistration::new(), - #[cfg(feature = "dhcpv4-hostname")] - hostname: &mut resources.hostname, - }; - - #[cfg(feature = "proto-ipv4")] - inner.set_config_v4(&mut socket, config.ipv4); - #[cfg(feature = "proto-ipv6")] - inner.set_config_v6(&mut socket, config.ipv6); - inner.apply_static_config(&mut socket); - - Self { - socket: RefCell::new(socket), - inner: RefCell::new(inner), - } - } - - fn with(&self, f: impl FnOnce(&SocketStack, &Inner) -> R) -> R { - f(&*self.socket.borrow(), &*self.inner.borrow()) +impl<'d> Stack<'d> { + fn with(&self, f: impl FnOnce(&Inner) -> R) -> R { + f(&*self.inner.borrow()) } - fn with_mut(&self, f: impl FnOnce(&mut SocketStack, &mut Inner) -> R) -> R { - f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) + fn with_mut(&self, f: impl FnOnce(&mut Inner) -> R) -> R { + f(&mut *self.inner.borrow_mut()) } /// Get the hardware address of the network interface. pub fn hardware_address(&self) -> HardwareAddress { - self.with(|_s, i| to_smoltcp_hardware_address(i.device.hardware_address()).0) + self.with(|i| i.hardware_address) } /// Get whether the link is up. pub fn is_link_up(&self) -> bool { - self.with(|_s, i| i.link_up) + self.with(|i| i.link_up) } /// Get whether the network stack has a valid IP configuration. @@ -420,15 +437,14 @@ impl Stack { /// // provisioning space for 3 sockets here: one for DHCP, one for DNS, and one for your code (e.g. TCP). /// // If you use more sockets you must increase this. If you don't enable DHCP or DNS you can decrease it. /// static RESOURCES: StaticCell> = StaticCell::new(); - /// static STACK: StaticCell = StaticCell::new(); - /// let stack = &*STACK.init(embassy_net::Stack::new( - /// device, + /// let (stack, runner) = embassy_net::new( + /// driver, /// config, /// RESOURCES.init(embassy_net::StackResources::new()), /// seed - /// )); - /// // Launch network task that runs `stack.run().await` - /// spawner.spawn(net_task(stack)).unwrap(); + /// ); + /// // Launch network task that runs `runner.run().await` + /// spawner.spawn(net_task(runner)).unwrap(); /// // Wait for DHCP config /// stack.wait_config_up().await; /// // use the network stack @@ -448,7 +464,7 @@ impl Stack { // when a config is applied (static or DHCP). trace!("Waiting for config up"); - self.with_mut(|_, i| { + self.with_mut(|i| { i.config_waker.register(cx.waker()); }); @@ -464,45 +480,33 @@ impl Stack { /// acquire an IP address, or Some if it has. #[cfg(feature = "proto-ipv4")] pub fn config_v4(&self) -> Option { - self.with(|_, i| i.static_v4.clone()) + self.with(|i| i.static_v4.clone()) } /// Get the current IPv6 configuration. #[cfg(feature = "proto-ipv6")] pub fn config_v6(&self) -> Option { - self.with(|_, i| i.static_v6.clone()) + self.with(|i| i.static_v6.clone()) } /// Set the IPv4 configuration. #[cfg(feature = "proto-ipv4")] pub fn set_config_v4(&self, config: ConfigV4) { - self.with_mut(|s, i| { - i.set_config_v4(s, config); - i.apply_static_config(s); + self.with_mut(|i| { + i.set_config_v4(config); + i.apply_static_config(); }) } /// Set the IPv6 configuration. #[cfg(feature = "proto-ipv6")] pub fn set_config_v6(&self, config: ConfigV6) { - self.with_mut(|s, i| { - i.set_config_v6(s, config); - i.apply_static_config(s); + self.with_mut(|i| { + i.set_config_v6(config); + i.apply_static_config(); }) } - /// Run the network stack. - /// - /// You must call this in a background task, to process network events. - pub async fn run(&self) -> ! { - poll_fn(|cx| { - self.with_mut(|s, i| i.poll(cx, s)); - Poll::<()>::Pending - }) - .await; - unreachable!() - } - /// Make a query for a given name and return the corresponding IP addresses. #[cfg(feature = "dns")] pub async fn dns_query( @@ -528,11 +532,11 @@ impl Stack { } let query = poll_fn(|cx| { - self.with_mut(|s, i| { - let socket = s.sockets.get_mut::(i.dns_socket); - match socket.start_query(s.iface.context(), name, qtype) { + self.with_mut(|i| { + let socket = i.sockets.get_mut::(i.dns_socket); + match socket.start_query(i.iface.context(), name, qtype) { Ok(handle) => { - s.waker.wake(); + i.waker.wake(); Poll::Ready(Ok(handle)) } Err(dns::StartQueryError::NoFreeSlot) => { @@ -569,17 +573,17 @@ impl Stack { } let drop = OnDrop::new(|| { - self.with_mut(|s, i| { - let socket = s.sockets.get_mut::(i.dns_socket); + self.with_mut(|i| { + let socket = i.sockets.get_mut::(i.dns_socket); socket.cancel_query(query); - s.waker.wake(); + i.waker.wake(); i.dns_waker.wake(); }) }); let res = poll_fn(|cx| { - self.with_mut(|s, i| { - let socket = s.sockets.get_mut::(i.dns_socket); + self.with_mut(|i| { + let socket = i.sockets.get_mut::(i.dns_socket); match socket.get_query_result(query) { Ok(addrs) => { i.dns_waker.wake(); @@ -604,104 +608,34 @@ impl Stack { } } -#[cfg(feature = "igmp")] -impl Stack { +#[cfg(feature = "multicast")] +impl<'d> Stack<'d> { /// Join a multicast group. - pub async fn join_multicast_group(&self, addr: T) -> Result - where - T: Into, - { - let addr = addr.into(); - - poll_fn(move |cx| self.poll_join_multicast_group(addr, cx)).await - } - - /// Join a multicast group. - /// - /// When the send queue is full, this method will return `Poll::Pending` - /// and register the current task to be notified when the queue has space available. - pub fn poll_join_multicast_group(&self, addr: T, cx: &mut Context<'_>) -> Poll> - where - T: Into, - { - let addr = addr.into(); - - self.with_mut(|s, i| { - let (_hardware_addr, medium) = to_smoltcp_hardware_address(i.device.hardware_address()); - let mut smoldev = DriverAdapter { - cx: Some(cx), - inner: &mut i.device, - medium, - }; - - match s - .iface - .join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) - { - Ok(announce_sent) => Poll::Ready(Ok(announce_sent)), - Err(MulticastError::Exhausted) => Poll::Pending, - Err(other) => Poll::Ready(Err(other)), - } - }) + pub fn join_multicast_group(&self, addr: impl Into) -> Result<(), MulticastError> { + self.with_mut(|i| i.iface.join_multicast_group(addr)) } /// Leave a multicast group. - pub async fn leave_multicast_group(&self, addr: T) -> Result - where - T: Into, - { - let addr = addr.into(); - - poll_fn(move |cx| self.poll_leave_multicast_group(addr, cx)).await - } - - /// Leave a multicast group. - /// - /// When the send queue is full, this method will return `Poll::Pending` - /// and register the current task to be notified when the queue has space available. - pub fn poll_leave_multicast_group(&self, addr: T, cx: &mut Context<'_>) -> Poll> - where - T: Into, - { - let addr = addr.into(); - - self.with_mut(|s, i| { - let (_hardware_addr, medium) = to_smoltcp_hardware_address(i.device.hardware_address()); - let mut smoldev = DriverAdapter { - cx: Some(cx), - inner: &mut i.device, - medium, - }; - - match s - .iface - .leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) - { - Ok(leave_sent) => Poll::Ready(Ok(leave_sent)), - Err(MulticastError::Exhausted) => Poll::Pending, - Err(other) => Poll::Ready(Err(other)), - } - }) + pub fn leave_multicast_group(&self, addr: impl Into) -> Result<(), MulticastError> { + self.with_mut(|i| i.iface.leave_multicast_group(addr)) } /// Get whether the network stack has joined the given multicast group. - pub fn has_multicast_group>(&self, addr: T) -> bool { - self.socket.borrow().iface.has_multicast_group(addr) + pub fn has_multicast_group(&self, addr: impl Into) -> bool { + self.with(|i| i.iface.has_multicast_group(addr)) } } -impl SocketStack { +impl Inner { #[allow(clippy::absurd_extreme_comparisons, dead_code)] pub fn get_local_port(&mut self) -> u16 { let res = self.next_local_port; self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; res } -} -impl Inner { #[cfg(feature = "proto-ipv4")] - pub fn set_config_v4(&mut self, _s: &mut SocketStack, config: ConfigV4) { + pub fn set_config_v4(&mut self, config: ConfigV4) { // Handle static config. self.static_v4 = match config.clone() { ConfigV4::None => None, @@ -717,12 +651,12 @@ impl Inner { // Create the socket if it doesn't exist. if self.dhcp_socket.is_none() { let socket = smoltcp::socket::dhcpv4::Socket::new(); - let handle = _s.sockets.add(socket); + let handle = self.sockets.add(socket); self.dhcp_socket = Some(handle); } // Configure it - let socket = _s.sockets.get_mut::(unwrap!(self.dhcp_socket)); + let socket = self.sockets.get_mut::(unwrap!(self.dhcp_socket)); socket.set_ignore_naks(c.ignore_naks); socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp)); socket.set_ports(c.server_port, c.client_port); @@ -731,19 +665,20 @@ impl Inner { socket.set_outgoing_options(&[]); #[cfg(feature = "dhcpv4-hostname")] if let Some(h) = c.hostname { - // safety: we just did set_outgoing_options([]) so we know the socket is no longer holding a reference. - let hostname = unsafe { &mut *self.hostname.get() }; + // safety: + // - we just did set_outgoing_options([]) so we know the socket is no longer holding a reference. + // - we know this pointer lives for as long as the stack exists, because `new()` borrows + // the resources for `'d`. Therefore it's OK to pass a reference to this to smoltcp. + let hostname = unsafe { &mut *self.hostname }; // create data - // safety: we know the buffer lives forever, new borrows the StackResources for 'static. - // also we won't modify it until next call to this function. - hostname.data[..h.len()].copy_from_slice(h.as_bytes()); - let data: &[u8] = &hostname.data[..h.len()]; - let data: &'static [u8] = unsafe { core::mem::transmute(data) }; + let data = hostname.data.write([0; MAX_HOSTNAME_LEN]); + data[..h.len()].copy_from_slice(h.as_bytes()); + let data: &[u8] = &data[..h.len()]; // set the option. - hostname.option = smoltcp::wire::DhcpOption { data, kind: 12 }; - socket.set_outgoing_options(core::slice::from_ref(&hostname.option)); + let option = hostname.option.write(smoltcp::wire::DhcpOption { data, kind: 12 }); + socket.set_outgoing_options(core::slice::from_ref(option)); } socket.reset(); @@ -751,7 +686,7 @@ impl Inner { _ => { // Remove DHCP socket if any. if let Some(socket) = self.dhcp_socket { - _s.sockets.remove(socket); + self.sockets.remove(socket); self.dhcp_socket = None; } } @@ -759,14 +694,14 @@ impl Inner { } #[cfg(feature = "proto-ipv6")] - pub fn set_config_v6(&mut self, _s: &mut SocketStack, config: ConfigV6) { + pub fn set_config_v6(&mut self, config: ConfigV6) { self.static_v6 = match config { ConfigV6::None => None, ConfigV6::Static(c) => Some(c), }; } - fn apply_static_config(&mut self, s: &mut SocketStack) { + fn apply_static_config(&mut self) { let mut addrs = Vec::new(); #[cfg(feature = "dns")] let mut dns_servers: Vec<_, 6> = Vec::new(); @@ -810,20 +745,20 @@ impl Inner { } // Apply addresses - s.iface.update_ip_addrs(|a| *a = addrs); + self.iface.update_ip_addrs(|a| *a = addrs); // Apply gateways #[cfg(feature = "proto-ipv4")] if let Some(gateway) = gateway_v4 { - unwrap!(s.iface.routes_mut().add_default_ipv4_route(gateway)); + unwrap!(self.iface.routes_mut().add_default_ipv4_route(gateway)); } else { - s.iface.routes_mut().remove_default_ipv4_route(); + self.iface.routes_mut().remove_default_ipv4_route(); } #[cfg(feature = "proto-ipv6")] if let Some(gateway) = gateway_v6 { - unwrap!(s.iface.routes_mut().add_default_ipv6_route(gateway)); + unwrap!(self.iface.routes_mut().add_default_ipv6_route(gateway)); } else { - s.iface.routes_mut().remove_default_ipv6_route(); + self.iface.routes_mut().remove_default_ipv6_route(); } // Apply DNS servers @@ -835,7 +770,7 @@ impl Inner { } else { dns_servers.len() }; - s.sockets + self.sockets .get_mut::(self.dns_socket) .update_servers(&dns_servers[..count]); } @@ -843,10 +778,10 @@ impl Inner { self.config_waker.wake(); } - fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { - s.waker.register(cx.waker()); + fn poll(&mut self, cx: &mut Context<'_>, driver: &mut D) { + self.waker.register(cx.waker()); - let (_hardware_addr, medium) = to_smoltcp_hardware_address(self.device.hardware_address()); + let (_hardware_addr, medium) = to_smoltcp_hardware_address(driver.hardware_address()); #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] { @@ -859,21 +794,21 @@ impl Inner { _ => false, }; if do_set { - s.iface.set_hardware_addr(_hardware_addr); + self.iface.set_hardware_addr(_hardware_addr); } } let timestamp = instant_to_smoltcp(Instant::now()); let mut smoldev = DriverAdapter { cx: Some(cx), - inner: &mut self.device, + inner: driver, medium, }; - s.iface.poll(timestamp, &mut smoldev, &mut s.sockets); + self.iface.poll(timestamp, &mut smoldev, &mut self.sockets); // Update link up let old_link_up = self.link_up; - self.link_up = self.device.link_state(cx) == LinkState::Up; + self.link_up = driver.link_state(cx) == LinkState::Up; // Print when changed if old_link_up != self.link_up { @@ -885,7 +820,7 @@ impl Inner { #[cfg(feature = "dhcpv4")] if let Some(dhcp_handle) = self.dhcp_socket { - let socket = s.sockets.get_mut::(dhcp_handle); + let socket = self.sockets.get_mut::(dhcp_handle); if self.link_up { if old_link_up != self.link_up { @@ -914,10 +849,10 @@ impl Inner { } if apply_config { - self.apply_static_config(s); + self.apply_static_config(); } - if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { + if let Some(poll_at) = self.iface.poll_at(timestamp, &mut self.sockets) { let t = pin!(Timer::at(instant_from_smoltcp(poll_at))); if t.poll(cx).is_ready() { cx.waker().wake_by_ref(); @@ -925,3 +860,17 @@ impl Inner { } } } + +impl<'d, D: Driver> Runner<'d, D> { + /// Run the network stack. + /// + /// You must call this in a background task, to process network events. + pub async fn run(&mut self) -> ! { + poll_fn(|cx| { + self.stack.with_mut(|i| i.poll(cx, &mut self.driver)); + Poll::<()>::Pending + }) + .await; + unreachable!() + } +} diff --git a/embassy-net/src/raw.rs b/embassy-net/src/raw.rs index 7ecd913e7..1098dc208 100644 --- a/embassy-net/src/raw.rs +++ b/embassy-net/src/raw.rs @@ -1,6 +1,5 @@ //! Raw sockets. -use core::cell::RefCell; use core::future::poll_fn; use core::mem; use core::task::{Context, Poll}; @@ -11,7 +10,7 @@ use smoltcp::socket::raw; pub use smoltcp::socket::raw::PacketMetadata; use smoltcp::wire::{IpProtocol, IpVersion}; -use crate::{SocketStack, Stack}; +use crate::Stack; /// Error returned by [`RawSocket::recv`] and [`RawSocket::send`]. #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -23,14 +22,14 @@ pub enum RecvError { /// An Raw socket. pub struct RawSocket<'a> { - stack: &'a RefCell, + stack: Stack<'a>, handle: SocketHandle, } impl<'a> RawSocket<'a> { /// Create a new Raw socket using the provided stack and buffers. pub fn new( - stack: &'a Stack, + stack: Stack<'a>, ip_version: IpVersion, ip_protocol: IpProtocol, rx_meta: &'a mut [PacketMetadata], @@ -38,31 +37,29 @@ impl<'a> RawSocket<'a> { tx_meta: &'a mut [PacketMetadata], tx_buffer: &'a mut [u8], ) -> Self { - let s = &mut *stack.socket.borrow_mut(); + let handle = stack.with_mut(|i| { + let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) }; + let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; + let tx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(tx_meta) }; + let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; + i.sockets.add(raw::Socket::new( + ip_version, + ip_protocol, + raw::PacketBuffer::new(rx_meta, rx_buffer), + raw::PacketBuffer::new(tx_meta, tx_buffer), + )) + }); - let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) }; - let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; - let tx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(tx_meta) }; - let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; - let handle = s.sockets.add(raw::Socket::new( - ip_version, - ip_protocol, - raw::PacketBuffer::new(rx_meta, rx_buffer), - raw::PacketBuffer::new(tx_meta, tx_buffer), - )); - - Self { - stack: &stack.socket, - handle, - } + Self { stack, handle } } fn with_mut(&self, f: impl FnOnce(&mut raw::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.borrow_mut(); - let socket = s.sockets.get_mut::(self.handle); - let res = f(socket, &mut s.iface); - s.waker.wake(); - res + self.stack.with_mut(|i| { + let socket = i.sockets.get_mut::(self.handle); + let res = f(socket, &mut i.iface); + i.waker.wake(); + res + }) } /// Receive a datagram. @@ -115,6 +112,10 @@ impl<'a> RawSocket<'a> { impl Drop for RawSocket<'_> { fn drop(&mut self) { - self.stack.borrow_mut().sockets.remove(self.handle); + self.stack.with_mut(|i| i.sockets.remove(self.handle)); } } + +fn _assert_covariant<'a, 'b: 'a>(x: RawSocket<'b>) -> RawSocket<'a> { + x +} diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index b2e3279cc..bcddbc95b 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -8,12 +8,10 @@ //! Incoming connections when no socket is listening are rejected. To accept many incoming //! connections, create many sockets and put them all into listening mode. -use core::cell::RefCell; use core::future::poll_fn; use core::mem; use core::task::Poll; -use embassy_net_driver::Driver; use embassy_time::Duration; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::tcp; @@ -21,7 +19,7 @@ pub use smoltcp::socket::tcp::State; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::time::duration_to_smoltcp; -use crate::{SocketStack, Stack}; +use crate::Stack; /// Error returned by TcpSocket read/write functions. #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -157,20 +155,18 @@ impl<'a> TcpWriter<'a> { impl<'a> TcpSocket<'a> { /// Create a new TCP socket on the given stack, with the given buffers. - pub fn new(stack: &'a Stack, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { - let s = &mut *stack.socket.borrow_mut(); - let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; - let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; - let handle = s.sockets.add(tcp::Socket::new( - tcp::SocketBuffer::new(rx_buffer), - tcp::SocketBuffer::new(tx_buffer), - )); + pub fn new(stack: Stack<'a>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { + let handle = stack.with_mut(|i| { + let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; + let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; + i.sockets.add(tcp::Socket::new( + tcp::SocketBuffer::new(rx_buffer), + tcp::SocketBuffer::new(tx_buffer), + )) + }); Self { - io: TcpIo { - stack: &stack.socket, - handle, - }, + io: TcpIo { stack: stack, handle }, } } @@ -228,7 +224,7 @@ impl<'a> TcpSocket<'a> { where T: Into, { - let local_port = self.io.stack.borrow_mut().get_local_port(); + let local_port = self.io.stack.with_mut(|i| i.get_local_port()); match { self.io @@ -401,31 +397,43 @@ impl<'a> TcpSocket<'a> { impl<'a> Drop for TcpSocket<'a> { fn drop(&mut self) { - self.io.stack.borrow_mut().sockets.remove(self.io.handle); + self.io.stack.with_mut(|i| i.sockets.remove(self.io.handle)); } } +fn _assert_covariant<'a, 'b: 'a>(x: TcpSocket<'b>) -> TcpSocket<'a> { + x +} +fn _assert_covariant_reader<'a, 'b: 'a>(x: TcpReader<'b>) -> TcpReader<'a> { + x +} +fn _assert_covariant_writer<'a, 'b: 'a>(x: TcpWriter<'b>) -> TcpWriter<'a> { + x +} + // ======================= #[derive(Copy, Clone)] struct TcpIo<'a> { - stack: &'a RefCell, + stack: Stack<'a>, handle: SocketHandle, } impl<'d> TcpIo<'d> { fn with(&self, f: impl FnOnce(&tcp::Socket, &Interface) -> R) -> R { - let s = &*self.stack.borrow(); - let socket = s.sockets.get::(self.handle); - f(socket, &s.iface) + self.stack.with(|i| { + let socket = i.sockets.get::(self.handle); + f(socket, &i.iface) + }) } fn with_mut(&mut self, f: impl FnOnce(&mut tcp::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.borrow_mut(); - let socket = s.sockets.get_mut::(self.handle); - let res = f(socket, &mut s.iface); - s.waker.wake(); - res + self.stack.with_mut(|i| { + let socket = i.sockets.get_mut::(self.handle); + let res = f(socket, &mut i.iface); + i.waker.wake(); + res + }) } async fn read(&mut self, buf: &mut [u8]) -> Result { @@ -676,15 +684,15 @@ pub mod client { /// TCP client connection pool compatible with `embedded-nal-async` traits. /// /// The pool is capable of managing up to N concurrent connections with tx and rx buffers according to TX_SZ and RX_SZ. - pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { - stack: &'d Stack, + pub struct TcpClient<'d, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { + stack: Stack<'d>, state: &'d TcpClientState, socket_timeout: Option, } - impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { + impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, N, TX_SZ, RX_SZ> { /// Create a new `TcpClient`. - pub fn new(stack: &'d Stack, state: &'d TcpClientState) -> Self { + pub fn new(stack: Stack<'d>, state: &'d TcpClientState) -> Self { Self { stack, state, @@ -701,8 +709,8 @@ pub mod client { } } - impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect - for TcpClient<'d, D, N, TX_SZ, RX_SZ> + impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect + for TcpClient<'d, N, TX_SZ, RX_SZ> { type Error = Error; type Connection<'m> = TcpConnection<'m, N, TX_SZ, RX_SZ> where Self: 'm; @@ -722,7 +730,7 @@ pub mod client { IpAddr::V6(_) => panic!("ipv6 support not enabled"), }; let remote_endpoint = (addr, remote.port()); - let mut socket = TcpConnection::new(&self.stack, self.state)?; + let mut socket = TcpConnection::new(self.stack, self.state)?; socket.socket.set_timeout(self.socket_timeout.clone()); socket .socket @@ -741,7 +749,7 @@ pub mod client { } impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpConnection<'d, N, TX_SZ, RX_SZ> { - fn new(stack: &'d Stack, state: &'d TcpClientState) -> Result { + fn new(stack: Stack<'d>, state: &'d TcpClientState) -> Result { let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?; Ok(Self { socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().1, &mut bufs.as_mut().0) }, diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 1d5360187..3eb6e2f83 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,17 +1,15 @@ //! UDP sockets. -use core::cell::RefCell; use core::future::poll_fn; use core::mem; use core::task::{Context, Poll}; -use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp; pub use smoltcp::socket::udp::{PacketMetadata, UdpMetadata}; use smoltcp::wire::IpListenEndpoint; -use crate::{SocketStack, Stack}; +use crate::Stack; /// Error returned by [`UdpSocket::bind`]. #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -43,34 +41,31 @@ pub enum RecvError { /// An UDP socket. pub struct UdpSocket<'a> { - stack: &'a RefCell, + stack: Stack<'a>, handle: SocketHandle, } impl<'a> UdpSocket<'a> { /// Create a new UDP socket using the provided stack and buffers. - pub fn new( - stack: &'a Stack, + pub fn new( + stack: Stack<'a>, rx_meta: &'a mut [PacketMetadata], rx_buffer: &'a mut [u8], tx_meta: &'a mut [PacketMetadata], tx_buffer: &'a mut [u8], ) -> Self { - let s = &mut *stack.socket.borrow_mut(); - - let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) }; - let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; - let tx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(tx_meta) }; - let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; - let handle = s.sockets.add(udp::Socket::new( - udp::PacketBuffer::new(rx_meta, rx_buffer), - udp::PacketBuffer::new(tx_meta, tx_buffer), - )); - - Self { - stack: &stack.socket, - handle, - } + let handle = stack.with_mut(|i| { + let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) }; + let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; + let tx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(tx_meta) }; + let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; + i.sockets.add(udp::Socket::new( + udp::PacketBuffer::new(rx_meta, rx_buffer), + udp::PacketBuffer::new(tx_meta, tx_buffer), + )) + }); + + Self { stack, handle } } /// Bind the socket to a local endpoint. @@ -82,7 +77,7 @@ impl<'a> UdpSocket<'a> { if endpoint.port == 0 { // If user didn't specify port allocate a dynamic port. - endpoint.port = self.stack.borrow_mut().get_local_port(); + endpoint.port = self.stack.with_mut(|i| i.get_local_port()); } match self.with_mut(|s, _| s.bind(endpoint)) { @@ -93,17 +88,19 @@ impl<'a> UdpSocket<'a> { } fn with(&self, f: impl FnOnce(&udp::Socket, &Interface) -> R) -> R { - let s = &*self.stack.borrow(); - let socket = s.sockets.get::(self.handle); - f(socket, &s.iface) + self.stack.with(|i| { + let socket = i.sockets.get::(self.handle); + f(socket, &i.iface) + }) } fn with_mut(&self, f: impl FnOnce(&mut udp::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.borrow_mut(); - let socket = s.sockets.get_mut::(self.handle); - let res = f(socket, &mut s.iface); - s.waker.wake(); - res + self.stack.with_mut(|i| { + let socket = i.sockets.get_mut::(self.handle); + let res = f(socket, &mut i.iface); + i.waker.wake(); + res + }) } /// Receive a datagram. @@ -298,6 +295,10 @@ impl<'a> UdpSocket<'a> { impl Drop for UdpSocket<'_> { fn drop(&mut self) { - self.stack.borrow_mut().sockets.remove(self.handle); + self.stack.with_mut(|i| i.sockets.remove(self.handle)); } } + +fn _assert_covariant<'a, 'b: 'a>(x: UdpSocket<'b>) -> UdpSocket<'a> { + x +} diff --git a/examples/nrf52840/src/bin/ethernet_enc28j60.rs b/examples/nrf52840/src/bin/ethernet_enc28j60.rs index 94cf09c88..0946492fe 100644 --- a/examples/nrf52840/src/bin/ethernet_enc28j60.rs +++ b/examples/nrf52840/src/bin/ethernet_enc28j60.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_net_enc28j60::Enc28j60; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::rng::Rng; @@ -23,11 +23,12 @@ bind_interrupts!(struct Irqs { #[embassy_executor::task] async fn net_task( - stack: &'static Stack< + mut runner: embassy_net::Runner< + 'static, Enc28j60, Output<'static>, Delay>, Output<'static>>, >, ) -> ! { - stack.run().await + runner.run().await } #[embassy_executor::main] @@ -67,12 +68,9 @@ async fn main(spawner: Spawner) { // Init network stack static RESOURCES: StaticCell> = StaticCell::new(); - static STACK: StaticCell< - Stack, Output<'static>, Delay>, Output<'static>>>, - > = StaticCell::new(); - let stack = STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // And now we can use it! diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index e56b215e3..b07adac1f 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -6,7 +6,7 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_nrf::rng::Rng; use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; use embassy_nrf::usb::Driver; @@ -39,8 +39,8 @@ async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -116,10 +116,9 @@ async fn main(spawner: Spawner) { // Init network stack static RESOURCES: StaticCell> = StaticCell::new(); - static STACK: StaticCell>> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // And now we can use it! diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index a3b69a99b..26eaf485e 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap, warn}; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; @@ -36,8 +36,8 @@ async fn wifi_task( } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, hosted::NetDriver<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -90,10 +90,9 @@ async fn main(spawner: Spawner) { // Init network stack static RESOURCES: StaticCell> = StaticCell::new(); - static STACK: StaticCell>> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // And now we can use it! diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs index 5335b6b51..929883884 100644 --- a/examples/nrf9160/src/bin/modem_tcp_client.rs +++ b/examples/nrf9160/src/bin/modem_tcp_client.rs @@ -46,15 +46,15 @@ async fn modem_task(runner: Runner<'static>) -> ! { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, embassy_net_nrf91::NetDriver<'static>>) -> ! { + runner.run().await } #[embassy_executor::task] async fn control_task( control: &'static context::Control<'static>, config: context::Config<'static>, - stack: &'static Stack>, + stack: Stack<'static>, ) { unwrap!(control.configure(&config).await); unwrap!( @@ -150,15 +150,9 @@ async fn main(spawner: Spawner) { // Init network stack static RESOURCES: StaticCell> = StaticCell::new(); - static STACK: StaticCell>> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( - device, - config, - RESOURCES.init(StackResources::<2>::new()), - seed, - )); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::<2>::new()), seed); - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); static CONTROL: StaticCell> = StaticCell::new(); let control = CONTROL.init(context::Control::new(control, 0).await); diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index aaa035a72..12003adbe 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -36,8 +36,8 @@ async fn ethernet_task( } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -71,17 +71,16 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( + let (stack, runner) = embassy_net::new( device, embassy_net::Config::dhcpv4(Default::default()), RESOURCES.init(StackResources::new()), seed, - )); + ); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(runner))); info!("Waiting for DHCP..."); let cfg = wait_for_config(stack).await; @@ -89,12 +88,12 @@ async fn main(spawner: Spawner) { 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))); + 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) { +async fn listen_task(stack: Stack<'static>, id: u8, port: u16) { let mut rx_buffer = [0; 4096]; let mut tx_buffer = [0; 4096]; let mut buf = [0; 4096]; @@ -131,7 +130,7 @@ async fn listen_task(stack: &'static Stack>, id: u8, port: u16) } } -async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { +async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { loop { if let Some(config) = stack.config_v4() { return config.clone(); diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 8e96a114c..d66a43a88 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -38,8 +38,8 @@ async fn ethernet_task( } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -74,17 +74,16 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( + let (stack, runner) = embassy_net::new( device, embassy_net::Config::dhcpv4(Default::default()), RESOURCES.init(StackResources::new()), seed, - )); + ); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(runner))); info!("Waiting for DHCP..."); let cfg = wait_for_config(stack).await; @@ -119,7 +118,7 @@ async fn main(spawner: Spawner) { } } -async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { +async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { loop { if let Some(config) = stack.config_v4() { return config.clone(); diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 40736bf3c..97d9bd4c9 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -37,8 +37,8 @@ async fn ethernet_task( } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -73,17 +73,16 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( + let (stack, runner) = embassy_net::new( device, embassy_net::Config::dhcpv4(Default::default()), RESOURCES.init(StackResources::new()), seed, - )); + ); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(runner))); info!("Waiting for DHCP..."); let cfg = wait_for_config(stack).await; @@ -128,7 +127,7 @@ async fn main(spawner: Spawner) { } } -async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { +async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { loop { if let Some(config) = stack.config_v4() { return config.clone(); diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index c79f01538..b1b5f9758 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -36,8 +36,8 @@ async fn ethernet_task( } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -71,17 +71,16 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( + let (stack, runner) = embassy_net::new( device, embassy_net::Config::dhcpv4(Default::default()), RESOURCES.init(StackResources::new()), seed, - )); + ); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(runner))); info!("Waiting for DHCP..."); let cfg = wait_for_config(stack).await; @@ -108,7 +107,7 @@ async fn main(spawner: Spawner) { } } -async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { +async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { loop { if let Some(config) = stack.config_v4() { return config.clone(); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 03c510f37..9a15125d4 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -8,7 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_rp::clocks::RoscRng; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; @@ -40,8 +40,8 @@ async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -108,11 +108,10 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // And now we can use it! diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index 00f404a9b..4c9651433 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -11,7 +11,7 @@ use cyw43_pio::PioSpi; use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Config, Stack, StackResources}; +use embassy_net::{Config, StackResources}; use embassy_rp::bind_interrupts; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Level, Output}; @@ -33,8 +33,8 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -80,16 +80,10 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( - net_device, - config, - RESOURCES.init(StackResources::new()), - seed, - )); - - unwrap!(spawner.spawn(net_task(stack))); + let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed); + + unwrap!(spawner.spawn(net_task(runner))); //control.start_ap_open("cyw43", 5).await; control.start_ap_wpa2("cyw43", "password", 5).await; diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index ab3529112..434f0074c 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -10,7 +10,6 @@ use core::str; use cyw43_pio::PioSpi; use defmt::*; use embassy_executor::Spawner; -use embassy_net::Stack; use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIO0}; @@ -28,8 +27,8 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index b2950d98a..7bf546e01 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -12,7 +12,7 @@ use cyw43_pio::PioSpi; use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Config, Stack, StackResources}; +use embassy_net::{Config, StackResources}; use embassy_rp::bind_interrupts; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Level, Output}; @@ -37,8 +37,8 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -84,16 +84,10 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( - net_device, - config, - RESOURCES.init(StackResources::new()), - seed, - )); - - unwrap!(spawner.spawn(net_task(stack))); + let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed); + + unwrap!(spawner.spawn(net_task(runner))); loop { match control diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs index b43be8905..1ae909917 100644 --- a/examples/rp/src/bin/wifi_webrequest.rs +++ b/examples/rp/src/bin/wifi_webrequest.rs @@ -13,7 +13,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::dns::DnsSocket; use embassy_net::tcp::client::{TcpClient, TcpClientState}; -use embassy_net::{Config, Stack, StackResources}; +use embassy_net::{Config, StackResources}; use embassy_rp::bind_interrupts; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Level, Output}; @@ -40,8 +40,8 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -87,16 +87,10 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( - net_device, - config, - RESOURCES.init(StackResources::new()), - seed, - )); - - unwrap!(spawner.spawn(net_task(stack))); + let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed); + + unwrap!(spawner.spawn(net_task(runner))); loop { match control diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 310e7264d..cefa5448c 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -1,7 +1,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; -use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources}; use embassy_net_tuntap::TunTapDevice; use embassy_time::Duration; use embedded_io_async::Write; @@ -22,8 +22,8 @@ struct Opts { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! { + runner.run().await } #[embassy_executor::task] @@ -50,12 +50,11 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - spawner.spawn(net_task(stack)).unwrap(); + spawner.spawn(net_task(runner)).unwrap(); // Then we can use it! let mut rx_buffer = [0; 4096]; diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index c9615ef35..a42c5dbb7 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -1,7 +1,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::dns::DnsQueryType; -use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources}; use embassy_net_tuntap::TunTapDevice; use heapless::Vec; use log::*; @@ -20,8 +20,8 @@ struct Opts { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! { + runner.run().await } #[embassy_executor::task] @@ -49,12 +49,11 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack: &Stack<_> = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - spawner.spawn(net_task(stack)).unwrap(); + spawner.spawn(net_task(runner)).unwrap(); let host = "example.com"; info!("querying host {:?}...", host); diff --git a/examples/std/src/bin/net_ppp.rs b/examples/std/src/bin/net_ppp.rs index c5c27c4a3..7d0f1327f 100644 --- a/examples/std/src/bin/net_ppp.rs +++ b/examples/std/src/bin/net_ppp.rs @@ -37,16 +37,12 @@ struct Opts { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, embassy_net_ppp::Device<'static>>) -> ! { + runner.run().await } #[embassy_executor::task] -async fn ppp_task( - stack: &'static Stack>, - mut runner: Runner<'static>, - port: SerialPort, -) -> ! { +async fn ppp_task(stack: Stack<'static>, mut runner: Runner<'static>, port: SerialPort) -> ! { let port = Async::new(port).unwrap(); let port = BufReader::new(port); let port = adapter::FromFutures::new(port); @@ -97,17 +93,16 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( + let (stack, net_runner) = embassy_net::new( device, Config::default(), // don't configure IP yet RESOURCES.init(StackResources::new()), seed, - )); + ); // Launch network task - spawner.spawn(net_task(stack)).unwrap(); + spawner.spawn(net_task(net_runner)).unwrap(); spawner.spawn(ppp_task(stack, runner, port)).unwrap(); // Then we can use it! diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index b2ba4915a..02d4d3efb 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -1,7 +1,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::udp::{PacketMetadata, UdpSocket}; -use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources}; use embassy_net_tuntap::TunTapDevice; use heapless::Vec; use log::*; @@ -20,8 +20,8 @@ struct Opts { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! { + runner.run().await } #[embassy_executor::task] @@ -48,12 +48,11 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - spawner.spawn(net_task(stack)).unwrap(); + spawner.spawn(net_task(runner)).unwrap(); // Then we can use it! let mut rx_meta = [PacketMetadata::EMPTY; 16]; diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index 39b29a449..5d36b739d 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs @@ -3,7 +3,7 @@ use core::fmt::Write as _; use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; -use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources}; use embassy_net_tuntap::TunTapDevice; use embassy_time::{Duration, Timer}; use embedded_io_async::Write as _; @@ -24,8 +24,8 @@ struct Opts { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! { + runner.run().await } #[derive(Default)] @@ -62,12 +62,11 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - spawner.spawn(net_task(stack)).unwrap(); + spawner.spawn(net_task(runner)).unwrap(); // Then we can use it! let mut rx_buffer = [0; 4096]; diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index 9388c64bf..baed96449 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net::{Ipv4Address, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; @@ -24,8 +24,8 @@ bind_interrupts!(struct Irqs { type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -88,12 +88,11 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // Ensure DHCP configuration is up before trying connect stack.wait_config_up().await; @@ -105,7 +104,7 @@ async fn main(spawner: Spawner) -> ! { let mut tx_buffer = [0; 4096]; loop { - let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs index 5c3c6c3ba..6e6bef08c 100644 --- a/examples/stm32f4/src/bin/eth_w5500.rs +++ b/examples/stm32f4/src/bin/eth_w5500.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net::{Ipv4Address, StackResources}; use embassy_net_wiznet::chip::W5500; use embassy_net_wiznet::{Device, Runner, State}; use embassy_stm32::exti::ExtiInput; @@ -31,8 +31,8 @@ async fn ethernet_task(runner: Runner<'static, W5500, EthernetSPI, ExtiInput<'st } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -92,12 +92,11 @@ async fn main(spawner: Spawner) -> ! { // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), //}); - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // Ensure DHCP configuration is up before trying connect stack.wait_config_up().await; diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 94e51c338..a9504ec04 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_stm32::rng::{self, Rng}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; @@ -31,8 +31,8 @@ async fn usb_ncm_task(class: Runner<'static, UsbDriver, MTU>) -> ! { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! { + runner.run().await } bind_interrupts!(struct Irqs { @@ -144,11 +144,10 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // And now we can use it! diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 2fd10c8fb..1f1eadf37 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net::{Ipv4Address, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; @@ -25,8 +25,8 @@ bind_interrupts!(struct Irqs { type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -89,12 +89,11 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // Ensure DHCP configuration is up before trying connect stack.wait_config_up().await; @@ -106,7 +105,7 @@ async fn main(spawner: Spawner) -> ! { let mut tx_buffer = [0; 4096]; loop { - let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 65cfad8c9..eee1632f5 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net::{Ipv4Address, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; @@ -28,8 +28,8 @@ bind_interrupts!(struct Irqs { type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -92,12 +92,11 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(runner))); // Ensure DHCP configuration is up before trying connect stack.wait_config_up().await; @@ -109,7 +108,7 @@ async fn main(spawner: Spawner) -> ! { let mut tx_buffer = [0; 1024]; loop { - let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index b2f8ed91e..ec3f2c000 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net::{Ipv4Address, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; @@ -24,8 +24,8 @@ bind_interrupts!(struct Irqs { type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -91,12 +91,11 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(runner))); // Ensure DHCP configuration is up before trying connect stack.wait_config_up().await; @@ -108,7 +107,7 @@ async fn main(spawner: Spawner) -> ! { let mut tx_buffer = [0; 1024]; loop { - let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 274c24ab1..24983ca85 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::client::{TcpClient, TcpClientState}; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; @@ -25,8 +25,8 @@ bind_interrupts!(struct Irqs { type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -91,12 +91,11 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // Ensure DHCP configuration is up before trying connect stack.wait_config_up().await; @@ -104,7 +103,7 @@ async fn main(spawner: Spawner) -> ! { info!("Network task initialized"); let state: TcpClientState<1, 1024, 1024> = TcpClientState::new(); - let client = TcpClient::new(&stack, &state); + let client = TcpClient::new(stack, &state); loop { // You need to start a server on the host machine, for example: `nc -l 8000` diff --git a/examples/stm32h7/src/bin/eth_client_mii.rs b/examples/stm32h7/src/bin/eth_client_mii.rs index aa6544f41..768d85993 100644 --- a/examples/stm32h7/src/bin/eth_client_mii.rs +++ b/examples/stm32h7/src/bin/eth_client_mii.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::client::{TcpClient, TcpClientState}; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; @@ -25,8 +25,8 @@ bind_interrupts!(struct Irqs { type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -97,12 +97,11 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // Ensure DHCP configuration is up before trying connect stack.wait_config_up().await; @@ -110,7 +109,7 @@ async fn main(spawner: Spawner) -> ! { info!("Network task initialized"); let state: TcpClientState<1, 1024, 1024> = TcpClientState::new(); - let client = TcpClient::new(&stack, &state); + let client = TcpClient::new(stack, &state); loop { // You need to start a server on the host machine, for example: `nc -l 8000` diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index bd633cecb..be4270ada 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -206,12 +206,11 @@ async fn main(spawner: Spawner) { }; // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, ip_cfg, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, ip_cfg, RESOURCES.init(StackResources::new()), seed); // Launch network task - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); let cfg = wait_for_config(stack).await; let local_addr = cfg.address.address(); @@ -274,7 +273,7 @@ async fn main(spawner: Spawner) { } } -async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { +async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { loop { if let Some(config) = stack.config_v4() { return config; @@ -323,8 +322,8 @@ async fn ethernet_task(runner: Runner<'static, SpeSpiCs, SpeInt, SpeRst>) -> ! { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { + runner.run().await } // same panicking *behavior* as `panic-probe` but doesn't print a panic message diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index d02bac91d..095d50c73 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_stm32::rng::Rng; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, rng, usb, Config}; @@ -36,8 +36,8 @@ async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -121,11 +121,10 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); // And now we can use it! diff --git a/tests/nrf/src/bin/ethernet_enc28j60_perf.rs b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs index 304754c0e..ed58627f1 100644 --- a/tests/nrf/src/bin/ethernet_enc28j60_perf.rs +++ b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs @@ -6,7 +6,7 @@ teleprobe_meta::timeout!(120); use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_net_enc28j60::Enc28j60; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::rng::Rng; @@ -25,8 +25,8 @@ bind_interrupts!(struct Irqs { type MyDriver = Enc28j60, Output<'static>, Delay>, Output<'static>>; #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, MyDriver>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -65,11 +65,10 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); perf_client::run( stack, diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 6632442f1..34fb8103b 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -6,7 +6,7 @@ teleprobe_meta::timeout!(120); use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_net::{Config, Stack, StackResources}; +use embassy_net::{Config, StackResources}; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; @@ -40,8 +40,8 @@ async fn wifi_task( type MyDriver = hosted::NetDriver<'static>; #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, MyDriver>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -86,16 +86,15 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( + let (stack, runner) = embassy_net::new( device, Config::dhcpv4(Default::default()), RESOURCES.init(StackResources::new()), seed, - )); + ); - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); perf_client::run( stack, diff --git a/tests/perf-client/src/lib.rs b/tests/perf-client/src/lib.rs index 54762379a..4bd9e5674 100644 --- a/tests/perf-client/src/lib.rs +++ b/tests/perf-client/src/lib.rs @@ -2,7 +2,6 @@ use defmt::{assert, *}; use embassy_futures::join::join; -use embassy_net::driver::Driver; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Stack}; use embassy_time::{with_timeout, Duration, Timer}; @@ -13,7 +12,7 @@ pub struct Expected { pub updown_kbps: usize, } -pub async fn run(stack: &Stack, expected: Expected) { +pub async fn run(stack: Stack<'_>, expected: Expected) { info!("Waiting for DHCP up..."); while stack.config_v4().is_none() { Timer::after_millis(100).await; @@ -38,7 +37,7 @@ const DOWNLOAD_PORT: u16 = 4321; const UPLOAD_PORT: u16 = 4322; const UPLOAD_DOWNLOAD_PORT: u16 = 4323; -async fn test_download(stack: &Stack) -> usize { +async fn test_download(stack: Stack<'_>) -> usize { info!("Testing download..."); let mut rx_buffer = [0; RX_BUFFER_SIZE]; @@ -78,7 +77,7 @@ async fn test_download(stack: &Stack) -> usize { kbps } -async fn test_upload(stack: &Stack) -> usize { +async fn test_upload(stack: Stack<'_>) -> usize { info!("Testing upload..."); let mut rx_buffer = [0; RX_BUFFER_SIZE]; @@ -118,7 +117,7 @@ async fn test_upload(stack: &Stack) -> usize { kbps } -async fn test_upload_download(stack: &Stack) -> usize { +async fn test_upload_download(stack: Stack<'_>) -> usize { info!("Testing upload+download..."); let mut rx_buffer = [0; RX_BUFFER_SIZE]; diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 11c8aa58c..30e4afb07 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -6,7 +6,7 @@ use cyw43::JoinOptions; use cyw43_pio::PioSpi; use defmt::{panic, *}; use embassy_executor::Spawner; -use embassy_net::{Config, Stack, StackResources}; +use embassy_net::{Config, StackResources}; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIO0}; use embassy_rp::pio::{InterruptHandler, Pio}; @@ -30,8 +30,8 @@ async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stati } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -70,16 +70,15 @@ async fn main(spawner: Spawner) { let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( + let (stack, runner) = embassy_net::new( net_device, Config::dhcpv4(Default::default()), RESOURCES.init(StackResources::new()), seed, - )); + ); - unwrap!(spawner.spawn(net_task(stack))); + unwrap!(spawner.spawn(net_task(runner))); loop { match control diff --git a/tests/rp/src/bin/ethernet_w5100s_perf.rs b/tests/rp/src/bin/ethernet_w5100s_perf.rs index f15f33743..ae2adfa55 100644 --- a/tests/rp/src/bin/ethernet_w5100s_perf.rs +++ b/tests/rp/src/bin/ethernet_w5100s_perf.rs @@ -5,7 +5,7 @@ teleprobe_meta::timeout!(120); use defmt::*; use embassy_executor::Spawner; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_net_wiznet::chip::W5100S; use embassy_net_wiznet::*; use embassy_rp::clocks::RoscRng; @@ -32,8 +32,8 @@ async fn ethernet_task( } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -67,17 +67,16 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - static STACK: StaticCell>> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new( + let (stack, runner) = embassy_net::new( device, embassy_net::Config::dhcpv4(Default::default()), RESOURCES.init(StackResources::new()), seed, - )); + ); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(runner))); perf_client::run( stack, diff --git a/tests/stm32/src/bin/eth.rs b/tests/stm32/src/bin/eth.rs index 9da514881..bf1922dde 100644 --- a/tests/stm32/src/bin/eth.rs +++ b/tests/stm32/src/bin/eth.rs @@ -6,7 +6,7 @@ mod common; use common::*; use embassy_executor::Spawner; -use embassy_net::{Stack, StackResources}; +use embassy_net::StackResources; use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; @@ -32,8 +32,8 @@ bind_interrupts!(struct Irqs { type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { - stack.run().await +async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -99,12 +99,11 @@ async fn main(spawner: Spawner) { //}); // Init network stack - static STACK: StaticCell> = StaticCell::new(); static RESOURCES: StaticCell> = StaticCell::new(); - let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(runner))); perf_client::run( stack, -- cgit From 73aa40a9b9ab13655aa39969494dd377f57b699e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 16 Sep 2024 21:27:34 +0200 Subject: Disable nrf52840 hil tests. --- ci.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci.sh b/ci.sh index 5e8bddb8c..50331bdba 100755 --- a/ci.sh +++ b/ci.sh @@ -290,8 +290,9 @@ cargo batch \ $BUILD_EXTRA -# temporarily disabled, bluepill board got bricked +# temporarily disabled, these boards are dead. rm -rf out/tests/stm32f103c8 +rm -rf out/tests/nrf52840-dk rm out/tests/stm32wb55rg/wpan_mac rm out/tests/stm32wb55rg/wpan_ble -- cgit From 6d89f2729ab7a6ee3dd78ccaef1b16677b5a9eee Mon Sep 17 00:00:00 2001 From: kingofpayne <43875454+kingofpayne@users.noreply.github.com> Date: Mon, 16 Sep 2024 22:07:56 +0200 Subject: boot: flash-erase-zero (#3344) Allow compatibility with devices whose flash erase set bytes to 0x00 instead of 0xFF, using a new flash-erase-zero feature. See issue #3342. --- docs/pages/bootloader.adoc | 2 ++ embassy-boot/Cargo.toml | 1 + embassy-boot/src/lib.rs | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/docs/pages/bootloader.adoc b/docs/pages/bootloader.adoc index 3b0cdb182..d8d50040b 100644 --- a/docs/pages/bootloader.adoc +++ b/docs/pages/bootloader.adoc @@ -19,6 +19,8 @@ The bootloader supports In general, the bootloader works on any platform that implements the `embedded-storage` traits for its internal flash, but may require custom initialization code to work. +STM32L0x1 devices require the `flash-erase-zero` feature to be enabled. + == Design image::bootloader_flash.png[Bootloader flash layout] diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml index d27fe763e..79ba6456a 100644 --- a/embassy-boot/Cargo.toml +++ b/embassy-boot/Cargo.toml @@ -47,6 +47,7 @@ ed25519-dalek = { version = "2", default-features = false, features = ["std", "r [features] ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] +flash-erase-zero = [] #Internal features _verify = [] diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs index b4f03e01e..8849055e8 100644 --- a/embassy-boot/src/lib.rs +++ b/embassy-boot/src/lib.rs @@ -14,7 +14,11 @@ mod test_flash; // The expected value of the flash after an erase // TODO: Use the value provided by NorFlash when available +#[cfg(not(feature = "flash-erase-zero"))] pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF; +#[cfg(feature = "flash-erase-zero")] +pub(crate) const STATE_ERASE_VALUE: u8 = 0x00; + pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; pub use firmware_updater::{ BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareState, FirmwareUpdater, FirmwareUpdaterConfig, -- cgit