diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-12-13 17:03:51 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-12-13 17:03:51 +0100 |
| commit | 5b65b0e84377338b615ba3fc16d0a96d2db5c206 (patch) | |
| tree | 627cc0abdfdf95f504728fceaaf9816575d05e84 | |
| parent | 47747d3b73f392e53ead8ff49cd09fd017df3215 (diff) | |
| parent | 790e4e1594d455d06c7303a628172244e78af0da (diff) | |
Merge pull request #1096 from embassy-rs/net-refactor2
net: remove packet pool
29 files changed, 1702 insertions, 1952 deletions
diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 4bdfd7720..5d86ca91e 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs | |||
| @@ -1,10 +1,7 @@ | |||
| 1 | use core::task::Waker; | 1 | use core::task::Context; |
| 2 | 2 | ||
| 3 | use smoltcp::phy::{Device as SmolDevice, DeviceCapabilities}; | 3 | use smoltcp::phy; |
| 4 | use smoltcp::time::Instant as SmolInstant; | 4 | pub use smoltcp::phy::{Checksum, ChecksumCapabilities, DeviceCapabilities, Medium}; |
| 5 | |||
| 6 | use crate::packet_pool::PacketBoxExt; | ||
| 7 | use crate::{Packet, PacketBox, PacketBuf}; | ||
| 8 | 5 | ||
| 9 | #[derive(PartialEq, Eq, Clone, Copy)] | 6 | #[derive(PartialEq, Eq, Clone, Copy)] |
| 10 | pub enum LinkState { | 7 | pub enum LinkState { |
| @@ -13,115 +10,133 @@ pub enum LinkState { | |||
| 13 | } | 10 | } |
| 14 | 11 | ||
| 15 | pub trait Device { | 12 | pub trait Device { |
| 16 | fn is_transmit_ready(&mut self) -> bool; | 13 | type RxToken<'a>: RxToken |
| 17 | fn transmit(&mut self, pkt: PacketBuf); | 14 | where |
| 18 | fn receive(&mut self) -> Option<PacketBuf>; | 15 | Self: 'a; |
| 16 | type TxToken<'a>: TxToken | ||
| 17 | where | ||
| 18 | Self: 'a; | ||
| 19 | 19 | ||
| 20 | fn register_waker(&mut self, waker: &Waker); | 20 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; |
| 21 | fn capabilities(&self) -> DeviceCapabilities; | 21 | fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>>; |
| 22 | fn link_state(&mut self) -> LinkState; | 22 | fn link_state(&mut self, cx: &mut Context) -> LinkState; |
| 23 | |||
| 24 | fn capabilities(&self) -> phy::DeviceCapabilities; | ||
| 23 | fn ethernet_address(&self) -> [u8; 6]; | 25 | fn ethernet_address(&self) -> [u8; 6]; |
| 24 | } | 26 | } |
| 25 | 27 | ||
| 26 | impl<T: ?Sized + Device> Device for &mut T { | 28 | impl<T: ?Sized + Device> Device for &mut T { |
| 27 | fn is_transmit_ready(&mut self) -> bool { | 29 | type RxToken<'a> = T::RxToken<'a> |
| 28 | T::is_transmit_ready(self) | 30 | where |
| 29 | } | 31 | Self: 'a; |
| 30 | fn transmit(&mut self, pkt: PacketBuf) { | 32 | type TxToken<'a> = T::TxToken<'a> |
| 31 | T::transmit(self, pkt) | 33 | where |
| 32 | } | 34 | Self: 'a; |
| 33 | fn receive(&mut self) -> Option<PacketBuf> { | 35 | |
| 34 | T::receive(self) | 36 | fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> { |
| 37 | T::transmit(self, cx) | ||
| 35 | } | 38 | } |
| 36 | fn register_waker(&mut self, waker: &Waker) { | 39 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { |
| 37 | T::register_waker(self, waker) | 40 | T::receive(self, cx) |
| 38 | } | 41 | } |
| 39 | fn capabilities(&self) -> DeviceCapabilities { | 42 | fn capabilities(&self) -> phy::DeviceCapabilities { |
| 40 | T::capabilities(self) | 43 | T::capabilities(self) |
| 41 | } | 44 | } |
| 42 | fn link_state(&mut self) -> LinkState { | 45 | fn link_state(&mut self, cx: &mut Context) -> LinkState { |
| 43 | T::link_state(self) | 46 | T::link_state(self, cx) |
| 44 | } | 47 | } |
| 45 | fn ethernet_address(&self) -> [u8; 6] { | 48 | fn ethernet_address(&self) -> [u8; 6] { |
| 46 | T::ethernet_address(self) | 49 | T::ethernet_address(self) |
| 47 | } | 50 | } |
| 48 | } | 51 | } |
| 49 | 52 | ||
| 50 | pub struct DeviceAdapter<D: Device> { | 53 | /// A token to receive a single network packet. |
| 51 | pub device: D, | 54 | pub trait RxToken { |
| 52 | caps: DeviceCapabilities, | 55 | /// Consumes the token to receive a single network packet. |
| 56 | /// | ||
| 57 | /// This method receives a packet and then calls the given closure `f` with the raw | ||
| 58 | /// packet bytes as argument. | ||
| 59 | fn consume<R, F>(self, f: F) -> R | ||
| 60 | where | ||
| 61 | F: FnOnce(&mut [u8]) -> R; | ||
| 53 | } | 62 | } |
| 54 | 63 | ||
| 55 | impl<D: Device> DeviceAdapter<D> { | 64 | /// A token to transmit a single network packet. |
| 56 | pub(crate) fn new(device: D) -> Self { | 65 | pub trait TxToken { |
| 57 | Self { | 66 | /// Consumes the token to send a single network packet. |
| 58 | caps: device.capabilities(), | 67 | /// |
| 59 | device, | 68 | /// This method constructs a transmit buffer of size `len` and calls the passed |
| 60 | } | 69 | /// closure `f` with a mutable reference to that buffer. The closure should construct |
| 61 | } | 70 | /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure |
| 71 | /// returns, the transmit buffer is sent out. | ||
| 72 | fn consume<R, F>(self, len: usize, f: F) -> R | ||
| 73 | where | ||
| 74 | F: FnOnce(&mut [u8]) -> R; | ||
| 75 | } | ||
| 76 | |||
| 77 | /////////////////////////// | ||
| 78 | |||
| 79 | pub(crate) struct DeviceAdapter<'d, 'c, T> | ||
| 80 | where | ||
| 81 | T: Device, | ||
| 82 | { | ||
| 83 | // must be Some when actually using this to rx/tx | ||
| 84 | pub cx: Option<&'d mut Context<'c>>, | ||
| 85 | pub inner: &'d mut T, | ||
| 62 | } | 86 | } |
| 63 | 87 | ||
| 64 | impl<D: Device> SmolDevice for DeviceAdapter<D> { | 88 | impl<'d, 'c, T> phy::Device for DeviceAdapter<'d, 'c, T> |
| 65 | type RxToken<'a> = RxToken where Self: 'a; | 89 | where |
| 66 | type TxToken<'a> = TxToken<'a, D> where Self: 'a; | 90 | T: Device, |
| 91 | { | ||
| 92 | type RxToken<'a> = RxTokenAdapter<T::RxToken<'a>> where Self: 'a; | ||
| 93 | type TxToken<'a> = TxTokenAdapter<T::TxToken<'a>> where Self: 'a; | ||
| 67 | 94 | ||
| 68 | fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { | 95 | fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { |
| 69 | let tx_pkt = PacketBox::new(Packet::new())?; | 96 | self.inner |
| 70 | let rx_pkt = self.device.receive()?; | 97 | .receive(self.cx.as_deref_mut().unwrap()) |
| 71 | let rx_token = RxToken { pkt: rx_pkt }; | 98 | .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) |
| 72 | let tx_token = TxToken { | ||
| 73 | device: &mut self.device, | ||
| 74 | pkt: tx_pkt, | ||
| 75 | }; | ||
| 76 | |||
| 77 | Some((rx_token, tx_token)) | ||
| 78 | } | 99 | } |
| 79 | 100 | ||
| 80 | /// Construct a transmit token. | 101 | /// Construct a transmit token. |
| 81 | fn transmit(&mut self) -> Option<Self::TxToken<'_>> { | 102 | fn transmit(&mut self) -> Option<Self::TxToken<'_>> { |
| 82 | if !self.device.is_transmit_ready() { | 103 | self.inner.transmit(self.cx.as_deref_mut().unwrap()).map(TxTokenAdapter) |
| 83 | return None; | ||
| 84 | } | ||
| 85 | |||
| 86 | let tx_pkt = PacketBox::new(Packet::new())?; | ||
| 87 | Some(TxToken { | ||
| 88 | device: &mut self.device, | ||
| 89 | pkt: tx_pkt, | ||
| 90 | }) | ||
| 91 | } | 104 | } |
| 92 | 105 | ||
| 93 | /// Get a description of device capabilities. | 106 | /// Get a description of device capabilities. |
| 94 | fn capabilities(&self) -> DeviceCapabilities { | 107 | fn capabilities(&self) -> phy::DeviceCapabilities { |
| 95 | self.caps.clone() | 108 | self.inner.capabilities() |
| 96 | } | 109 | } |
| 97 | } | 110 | } |
| 98 | 111 | ||
| 99 | pub struct RxToken { | 112 | pub(crate) struct RxTokenAdapter<T>(T) |
| 100 | pkt: PacketBuf, | 113 | where |
| 101 | } | 114 | T: RxToken; |
| 102 | 115 | ||
| 103 | impl smoltcp::phy::RxToken for RxToken { | 116 | impl<T> phy::RxToken for RxTokenAdapter<T> |
| 104 | fn consume<R, F>(mut self, _timestamp: SmolInstant, f: F) -> smoltcp::Result<R> | 117 | where |
| 118 | T: RxToken, | ||
| 119 | { | ||
| 120 | fn consume<R, F>(self, _timestamp: smoltcp::time::Instant, f: F) -> smoltcp::Result<R> | ||
| 105 | where | 121 | where |
| 106 | F: FnOnce(&mut [u8]) -> smoltcp::Result<R>, | 122 | F: FnOnce(&mut [u8]) -> smoltcp::Result<R>, |
| 107 | { | 123 | { |
| 108 | f(&mut self.pkt) | 124 | self.0.consume(|buf| f(buf)) |
| 109 | } | 125 | } |
| 110 | } | 126 | } |
| 111 | 127 | ||
| 112 | pub struct TxToken<'a, D: Device> { | 128 | pub(crate) struct TxTokenAdapter<T>(T) |
| 113 | device: &'a mut D, | 129 | where |
| 114 | pkt: PacketBox, | 130 | T: TxToken; |
| 115 | } | ||
| 116 | 131 | ||
| 117 | impl<'a, D: Device> smoltcp::phy::TxToken for TxToken<'a, D> { | 132 | impl<T> phy::TxToken for TxTokenAdapter<T> |
| 118 | fn consume<R, F>(self, _timestamp: SmolInstant, len: usize, f: F) -> smoltcp::Result<R> | 133 | where |
| 134 | T: TxToken, | ||
| 135 | { | ||
| 136 | fn consume<R, F>(self, _timestamp: smoltcp::time::Instant, len: usize, f: F) -> smoltcp::Result<R> | ||
| 119 | where | 137 | where |
| 120 | F: FnOnce(&mut [u8]) -> smoltcp::Result<R>, | 138 | F: FnOnce(&mut [u8]) -> smoltcp::Result<R>, |
| 121 | { | 139 | { |
| 122 | let mut buf = self.pkt.slice(0..len); | 140 | self.0.consume(len, |buf| f(buf)) |
| 123 | let r = f(&mut buf)?; | ||
| 124 | self.device.transmit(buf); | ||
| 125 | Ok(r) | ||
| 126 | } | 141 | } |
| 127 | } | 142 | } |
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index edb969842..afe0d6da0 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs | |||
| @@ -8,22 +8,30 @@ | |||
| 8 | // This mod MUST go first, so that the others see its macros. | 8 | // This mod MUST go first, so that the others see its macros. |
| 9 | pub(crate) mod fmt; | 9 | pub(crate) mod fmt; |
| 10 | 10 | ||
| 11 | mod device; | 11 | pub mod device; |
| 12 | mod packet_pool; | ||
| 13 | mod stack; | ||
| 14 | |||
| 15 | pub use device::{Device, LinkState}; | ||
| 16 | pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU}; | ||
| 17 | pub use stack::{Config, ConfigStrategy, Stack, StackResources}; | ||
| 18 | |||
| 19 | #[cfg(feature = "tcp")] | 12 | #[cfg(feature = "tcp")] |
| 20 | pub mod tcp; | 13 | pub mod tcp; |
| 21 | |||
| 22 | #[cfg(feature = "udp")] | 14 | #[cfg(feature = "udp")] |
| 23 | pub mod udp; | 15 | pub mod udp; |
| 24 | 16 | ||
| 17 | use core::cell::RefCell; | ||
| 18 | use core::future::{poll_fn, Future}; | ||
| 19 | use core::task::{Context, Poll}; | ||
| 20 | |||
| 21 | use embassy_sync::waitqueue::WakerRegistration; | ||
| 22 | use embassy_time::{Instant, Timer}; | ||
| 23 | use futures::pin_mut; | ||
| 24 | use heapless::Vec; | ||
| 25 | #[cfg(feature = "dhcpv4")] | ||
| 26 | use smoltcp::iface::SocketHandle; | ||
| 27 | use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; | ||
| 28 | #[cfg(feature = "medium-ethernet")] | ||
| 29 | use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; | ||
| 30 | #[cfg(feature = "medium-ethernet")] | ||
| 31 | use smoltcp::phy::Medium; | ||
| 32 | #[cfg(feature = "dhcpv4")] | ||
| 33 | use smoltcp::socket::dhcpv4; | ||
| 25 | // smoltcp reexports | 34 | // smoltcp reexports |
| 26 | pub use smoltcp::phy::{DeviceCapabilities, Medium}; | ||
| 27 | pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; | 35 | pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; |
| 28 | #[cfg(feature = "medium-ethernet")] | 36 | #[cfg(feature = "medium-ethernet")] |
| 29 | pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; | 37 | pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; |
| @@ -32,3 +40,288 @@ pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; | |||
| 32 | pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; | 40 | pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; |
| 33 | #[cfg(feature = "udp")] | 41 | #[cfg(feature = "udp")] |
| 34 | pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; | 42 | pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; |
| 43 | |||
| 44 | use crate::device::{Device, DeviceAdapter, LinkState}; | ||
| 45 | |||
| 46 | const LOCAL_PORT_MIN: u16 = 1025; | ||
| 47 | const LOCAL_PORT_MAX: u16 = 65535; | ||
| 48 | |||
| 49 | pub struct StackResources<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> { | ||
| 50 | addresses: [IpCidr; ADDR], | ||
| 51 | sockets: [SocketStorage<'static>; SOCK], | ||
| 52 | |||
| 53 | #[cfg(feature = "medium-ethernet")] | ||
| 54 | routes: [Option<(IpCidr, Route)>; 1], | ||
| 55 | #[cfg(feature = "medium-ethernet")] | ||
| 56 | neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR], | ||
| 57 | } | ||
| 58 | |||
| 59 | impl<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> StackResources<ADDR, SOCK, NEIGHBOR> { | ||
| 60 | pub fn new() -> Self { | ||
| 61 | Self { | ||
| 62 | addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR], | ||
| 63 | sockets: [SocketStorage::EMPTY; SOCK], | ||
| 64 | #[cfg(feature = "medium-ethernet")] | ||
| 65 | routes: [None; 1], | ||
| 66 | #[cfg(feature = "medium-ethernet")] | ||
| 67 | neighbor_cache: [None; NEIGHBOR], | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 73 | pub struct Config { | ||
| 74 | pub address: Ipv4Cidr, | ||
| 75 | pub gateway: Option<Ipv4Address>, | ||
| 76 | pub dns_servers: Vec<Ipv4Address, 3>, | ||
| 77 | } | ||
| 78 | |||
| 79 | pub enum ConfigStrategy { | ||
| 80 | Static(Config), | ||
| 81 | #[cfg(feature = "dhcpv4")] | ||
| 82 | Dhcp, | ||
| 83 | } | ||
| 84 | |||
| 85 | pub struct Stack<D: Device> { | ||
| 86 | pub(crate) socket: RefCell<SocketStack>, | ||
| 87 | inner: RefCell<Inner<D>>, | ||
| 88 | } | ||
| 89 | |||
| 90 | struct Inner<D: Device> { | ||
| 91 | device: D, | ||
| 92 | link_up: bool, | ||
| 93 | config: Option<Config>, | ||
| 94 | #[cfg(feature = "dhcpv4")] | ||
| 95 | dhcp_socket: Option<SocketHandle>, | ||
| 96 | } | ||
| 97 | |||
| 98 | pub(crate) struct SocketStack { | ||
| 99 | pub(crate) sockets: SocketSet<'static>, | ||
| 100 | pub(crate) iface: Interface<'static>, | ||
| 101 | pub(crate) waker: WakerRegistration, | ||
| 102 | next_local_port: u16, | ||
| 103 | } | ||
| 104 | |||
| 105 | impl<D: Device + 'static> Stack<D> { | ||
| 106 | pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>( | ||
| 107 | mut device: D, | ||
| 108 | config: ConfigStrategy, | ||
| 109 | resources: &'static mut StackResources<ADDR, SOCK, NEIGH>, | ||
| 110 | random_seed: u64, | ||
| 111 | ) -> Self { | ||
| 112 | #[cfg(feature = "medium-ethernet")] | ||
| 113 | let medium = device.capabilities().medium; | ||
| 114 | |||
| 115 | #[cfg(feature = "medium-ethernet")] | ||
| 116 | let ethernet_addr = if medium == Medium::Ethernet { | ||
| 117 | device.ethernet_address() | ||
| 118 | } else { | ||
| 119 | [0, 0, 0, 0, 0, 0] | ||
| 120 | }; | ||
| 121 | |||
| 122 | let mut b = InterfaceBuilder::new(); | ||
| 123 | b = b.ip_addrs(&mut resources.addresses[..]); | ||
| 124 | b = b.random_seed(random_seed); | ||
| 125 | |||
| 126 | #[cfg(feature = "medium-ethernet")] | ||
| 127 | if medium == Medium::Ethernet { | ||
| 128 | b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr))); | ||
| 129 | b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); | ||
| 130 | b = b.routes(Routes::new(&mut resources.routes[..])); | ||
| 131 | } | ||
| 132 | |||
| 133 | let iface = b.finalize(&mut DeviceAdapter { | ||
| 134 | inner: &mut device, | ||
| 135 | cx: None, | ||
| 136 | }); | ||
| 137 | |||
| 138 | let sockets = SocketSet::new(&mut resources.sockets[..]); | ||
| 139 | |||
| 140 | let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; | ||
| 141 | |||
| 142 | let mut inner = Inner { | ||
| 143 | device, | ||
| 144 | link_up: false, | ||
| 145 | config: None, | ||
| 146 | #[cfg(feature = "dhcpv4")] | ||
| 147 | dhcp_socket: None, | ||
| 148 | }; | ||
| 149 | let mut socket = SocketStack { | ||
| 150 | sockets, | ||
| 151 | iface, | ||
| 152 | waker: WakerRegistration::new(), | ||
| 153 | next_local_port, | ||
| 154 | }; | ||
| 155 | |||
| 156 | match config { | ||
| 157 | ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config), | ||
| 158 | #[cfg(feature = "dhcpv4")] | ||
| 159 | ConfigStrategy::Dhcp => { | ||
| 160 | let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new()); | ||
| 161 | inner.dhcp_socket = Some(handle); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | Self { | ||
| 166 | socket: RefCell::new(socket), | ||
| 167 | inner: RefCell::new(inner), | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | fn with<R>(&self, f: impl FnOnce(&SocketStack, &Inner<D>) -> R) -> R { | ||
| 172 | f(&*self.socket.borrow(), &*self.inner.borrow()) | ||
| 173 | } | ||
| 174 | |||
| 175 | fn with_mut<R>(&self, f: impl FnOnce(&mut SocketStack, &mut Inner<D>) -> R) -> R { | ||
| 176 | f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) | ||
| 177 | } | ||
| 178 | |||
| 179 | pub fn ethernet_address(&self) -> [u8; 6] { | ||
| 180 | self.with(|_s, i| i.device.ethernet_address()) | ||
| 181 | } | ||
| 182 | |||
| 183 | pub fn is_link_up(&self) -> bool { | ||
| 184 | self.with(|_s, i| i.link_up) | ||
| 185 | } | ||
| 186 | |||
| 187 | pub fn is_config_up(&self) -> bool { | ||
| 188 | self.with(|_s, i| i.config.is_some()) | ||
| 189 | } | ||
| 190 | |||
| 191 | pub fn config(&self) -> Option<Config> { | ||
| 192 | self.with(|_s, i| i.config.clone()) | ||
| 193 | } | ||
| 194 | |||
| 195 | pub async fn run(&self) -> ! { | ||
| 196 | poll_fn(|cx| { | ||
| 197 | self.with_mut(|s, i| i.poll(cx, s)); | ||
| 198 | Poll::<()>::Pending | ||
| 199 | }) | ||
| 200 | .await; | ||
| 201 | unreachable!() | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | impl SocketStack { | ||
| 206 | #[allow(clippy::absurd_extreme_comparisons)] | ||
| 207 | pub fn get_local_port(&mut self) -> u16 { | ||
| 208 | let res = self.next_local_port; | ||
| 209 | self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; | ||
| 210 | res | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | impl<D: Device + 'static> Inner<D> { | ||
| 215 | fn apply_config(&mut self, s: &mut SocketStack, config: Config) { | ||
| 216 | #[cfg(feature = "medium-ethernet")] | ||
| 217 | let medium = self.device.capabilities().medium; | ||
| 218 | |||
| 219 | debug!("Acquired IP configuration:"); | ||
| 220 | |||
| 221 | debug!(" IP address: {}", config.address); | ||
| 222 | self.set_ipv4_addr(s, config.address); | ||
| 223 | |||
| 224 | #[cfg(feature = "medium-ethernet")] | ||
| 225 | if medium == Medium::Ethernet { | ||
| 226 | if let Some(gateway) = config.gateway { | ||
| 227 | debug!(" Default gateway: {}", gateway); | ||
| 228 | s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap(); | ||
| 229 | } else { | ||
| 230 | debug!(" Default gateway: None"); | ||
| 231 | s.iface.routes_mut().remove_default_ipv4_route(); | ||
| 232 | } | ||
| 233 | } | ||
| 234 | for (i, s) in config.dns_servers.iter().enumerate() { | ||
| 235 | debug!(" DNS server {}: {}", i, s); | ||
| 236 | } | ||
| 237 | |||
| 238 | self.config = Some(config) | ||
| 239 | } | ||
| 240 | |||
| 241 | #[allow(unused)] // used only with dhcp | ||
| 242 | fn unapply_config(&mut self, s: &mut SocketStack) { | ||
| 243 | #[cfg(feature = "medium-ethernet")] | ||
| 244 | let medium = self.device.capabilities().medium; | ||
| 245 | |||
| 246 | debug!("Lost IP configuration"); | ||
| 247 | self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); | ||
| 248 | #[cfg(feature = "medium-ethernet")] | ||
| 249 | if medium == Medium::Ethernet { | ||
| 250 | s.iface.routes_mut().remove_default_ipv4_route(); | ||
| 251 | } | ||
| 252 | self.config = None | ||
| 253 | } | ||
| 254 | |||
| 255 | fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) { | ||
| 256 | s.iface.update_ip_addrs(|addrs| { | ||
| 257 | let dest = addrs.iter_mut().next().unwrap(); | ||
| 258 | *dest = IpCidr::Ipv4(cidr); | ||
| 259 | }); | ||
| 260 | } | ||
| 261 | |||
| 262 | fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { | ||
| 263 | s.waker.register(cx.waker()); | ||
| 264 | |||
| 265 | let timestamp = instant_to_smoltcp(Instant::now()); | ||
| 266 | let mut smoldev = DeviceAdapter { | ||
| 267 | cx: Some(cx), | ||
| 268 | inner: &mut self.device, | ||
| 269 | }; | ||
| 270 | if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() { | ||
| 271 | // If poll() returns error, it may not be done yet, so poll again later. | ||
| 272 | cx.waker().wake_by_ref(); | ||
| 273 | return; | ||
| 274 | } | ||
| 275 | |||
| 276 | // Update link up | ||
| 277 | let old_link_up = self.link_up; | ||
| 278 | self.link_up = self.device.link_state(cx) == LinkState::Up; | ||
| 279 | |||
| 280 | // Print when changed | ||
| 281 | if old_link_up != self.link_up { | ||
| 282 | info!("link_up = {:?}", self.link_up); | ||
| 283 | } | ||
| 284 | |||
| 285 | #[cfg(feature = "dhcpv4")] | ||
| 286 | if let Some(dhcp_handle) = self.dhcp_socket { | ||
| 287 | let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle); | ||
| 288 | |||
| 289 | if self.link_up { | ||
| 290 | match socket.poll() { | ||
| 291 | None => {} | ||
| 292 | Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), | ||
| 293 | Some(dhcpv4::Event::Configured(config)) => { | ||
| 294 | let config = Config { | ||
| 295 | address: config.address, | ||
| 296 | gateway: config.router, | ||
| 297 | dns_servers: config.dns_servers, | ||
| 298 | }; | ||
| 299 | self.apply_config(s, config) | ||
| 300 | } | ||
| 301 | } | ||
| 302 | } else if old_link_up { | ||
| 303 | socket.reset(); | ||
| 304 | self.unapply_config(s); | ||
| 305 | } | ||
| 306 | } | ||
| 307 | //if old_link_up || self.link_up { | ||
| 308 | // self.poll_configurator(timestamp) | ||
| 309 | //} | ||
| 310 | |||
| 311 | if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { | ||
| 312 | let t = Timer::at(instant_from_smoltcp(poll_at)); | ||
| 313 | pin_mut!(t); | ||
| 314 | if t.poll(cx).is_ready() { | ||
| 315 | cx.waker().wake_by_ref(); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | fn instant_to_smoltcp(instant: Instant) -> SmolInstant { | ||
| 322 | SmolInstant::from_millis(instant.as_millis() as i64) | ||
| 323 | } | ||
| 324 | |||
| 325 | fn instant_from_smoltcp(instant: SmolInstant) -> Instant { | ||
| 326 | Instant::from_millis(instant.total_millis() as u64) | ||
| 327 | } | ||
diff --git a/embassy-net/src/packet_pool.rs b/embassy-net/src/packet_pool.rs deleted file mode 100644 index cb8a1316c..000000000 --- a/embassy-net/src/packet_pool.rs +++ /dev/null | |||
| @@ -1,107 +0,0 @@ | |||
| 1 | use core::ops::{Deref, DerefMut, Range}; | ||
| 2 | |||
| 3 | use as_slice::{AsMutSlice, AsSlice}; | ||
| 4 | use atomic_pool::{pool, Box}; | ||
| 5 | |||
| 6 | pub const MTU: usize = 1516; | ||
| 7 | |||
| 8 | #[cfg(feature = "pool-4")] | ||
| 9 | pub const PACKET_POOL_SIZE: usize = 4; | ||
| 10 | |||
| 11 | #[cfg(feature = "pool-8")] | ||
| 12 | pub const PACKET_POOL_SIZE: usize = 8; | ||
| 13 | |||
| 14 | #[cfg(feature = "pool-16")] | ||
| 15 | pub const PACKET_POOL_SIZE: usize = 16; | ||
| 16 | |||
| 17 | #[cfg(feature = "pool-32")] | ||
| 18 | pub const PACKET_POOL_SIZE: usize = 32; | ||
| 19 | |||
| 20 | #[cfg(feature = "pool-64")] | ||
| 21 | pub const PACKET_POOL_SIZE: usize = 64; | ||
| 22 | |||
| 23 | #[cfg(feature = "pool-128")] | ||
| 24 | pub const PACKET_POOL_SIZE: usize = 128; | ||
| 25 | |||
| 26 | pool!(pub PacketPool: [Packet; PACKET_POOL_SIZE]); | ||
| 27 | pub type PacketBox = Box<PacketPool>; | ||
| 28 | |||
| 29 | #[repr(align(4))] | ||
| 30 | pub struct Packet(pub [u8; MTU]); | ||
| 31 | |||
| 32 | impl Packet { | ||
| 33 | pub const fn new() -> Self { | ||
| 34 | Self([0; MTU]) | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | pub trait PacketBoxExt { | ||
| 39 | fn slice(self, range: Range<usize>) -> PacketBuf; | ||
| 40 | } | ||
| 41 | |||
| 42 | impl PacketBoxExt for PacketBox { | ||
| 43 | fn slice(self, range: Range<usize>) -> PacketBuf { | ||
| 44 | PacketBuf { packet: self, range } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | impl AsSlice for Packet { | ||
| 49 | type Element = u8; | ||
| 50 | |||
| 51 | fn as_slice(&self) -> &[Self::Element] { | ||
| 52 | &self.deref()[..] | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | impl AsMutSlice for Packet { | ||
| 57 | fn as_mut_slice(&mut self) -> &mut [Self::Element] { | ||
| 58 | &mut self.deref_mut()[..] | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | impl Deref for Packet { | ||
| 63 | type Target = [u8; MTU]; | ||
| 64 | |||
| 65 | fn deref(&self) -> &[u8; MTU] { | ||
| 66 | &self.0 | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | impl DerefMut for Packet { | ||
| 71 | fn deref_mut(&mut self) -> &mut [u8; MTU] { | ||
| 72 | &mut self.0 | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | pub struct PacketBuf { | ||
| 77 | packet: PacketBox, | ||
| 78 | range: Range<usize>, | ||
| 79 | } | ||
| 80 | |||
| 81 | impl AsSlice for PacketBuf { | ||
| 82 | type Element = u8; | ||
| 83 | |||
| 84 | fn as_slice(&self) -> &[Self::Element] { | ||
| 85 | &self.packet[self.range.clone()] | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | impl AsMutSlice for PacketBuf { | ||
| 90 | fn as_mut_slice(&mut self) -> &mut [Self::Element] { | ||
| 91 | &mut self.packet[self.range.clone()] | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | impl Deref for PacketBuf { | ||
| 96 | type Target = [u8]; | ||
| 97 | |||
| 98 | fn deref(&self) -> &[u8] { | ||
| 99 | &self.packet[self.range.clone()] | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | impl DerefMut for PacketBuf { | ||
| 104 | fn deref_mut(&mut self) -> &mut [u8] { | ||
| 105 | &mut self.packet[self.range.clone()] | ||
| 106 | } | ||
| 107 | } | ||
diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs deleted file mode 100644 index 5c4fb0442..000000000 --- a/embassy-net/src/stack.rs +++ /dev/null | |||
| @@ -1,302 +0,0 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::future::{poll_fn, Future}; | ||
| 3 | use core::task::{Context, Poll}; | ||
| 4 | |||
| 5 | use embassy_sync::waitqueue::WakerRegistration; | ||
| 6 | use embassy_time::{Instant, Timer}; | ||
| 7 | use futures::pin_mut; | ||
| 8 | use heapless::Vec; | ||
| 9 | #[cfg(feature = "dhcpv4")] | ||
| 10 | use smoltcp::iface::SocketHandle; | ||
| 11 | use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; | ||
| 12 | #[cfg(feature = "medium-ethernet")] | ||
| 13 | use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; | ||
| 14 | #[cfg(feature = "medium-ethernet")] | ||
| 15 | use smoltcp::phy::{Device as _, Medium}; | ||
| 16 | #[cfg(feature = "dhcpv4")] | ||
| 17 | use smoltcp::socket::dhcpv4; | ||
| 18 | use smoltcp::time::Instant as SmolInstant; | ||
| 19 | #[cfg(feature = "medium-ethernet")] | ||
| 20 | use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress}; | ||
| 21 | use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr}; | ||
| 22 | |||
| 23 | use crate::device::{Device, DeviceAdapter, LinkState}; | ||
| 24 | |||
| 25 | const LOCAL_PORT_MIN: u16 = 1025; | ||
| 26 | const LOCAL_PORT_MAX: u16 = 65535; | ||
| 27 | |||
| 28 | pub struct StackResources<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> { | ||
| 29 | addresses: [IpCidr; ADDR], | ||
| 30 | sockets: [SocketStorage<'static>; SOCK], | ||
| 31 | |||
| 32 | #[cfg(feature = "medium-ethernet")] | ||
| 33 | routes: [Option<(IpCidr, Route)>; 1], | ||
| 34 | #[cfg(feature = "medium-ethernet")] | ||
| 35 | neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR], | ||
| 36 | } | ||
| 37 | |||
| 38 | impl<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> StackResources<ADDR, SOCK, NEIGHBOR> { | ||
| 39 | pub fn new() -> Self { | ||
| 40 | Self { | ||
| 41 | addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR], | ||
| 42 | sockets: [SocketStorage::EMPTY; SOCK], | ||
| 43 | #[cfg(feature = "medium-ethernet")] | ||
| 44 | routes: [None; 1], | ||
| 45 | #[cfg(feature = "medium-ethernet")] | ||
| 46 | neighbor_cache: [None; NEIGHBOR], | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 52 | pub struct Config { | ||
| 53 | pub address: Ipv4Cidr, | ||
| 54 | pub gateway: Option<Ipv4Address>, | ||
| 55 | pub dns_servers: Vec<Ipv4Address, 3>, | ||
| 56 | } | ||
| 57 | |||
| 58 | pub enum ConfigStrategy { | ||
| 59 | Static(Config), | ||
| 60 | #[cfg(feature = "dhcpv4")] | ||
| 61 | Dhcp, | ||
| 62 | } | ||
| 63 | |||
| 64 | pub struct Stack<D: Device> { | ||
| 65 | pub(crate) socket: RefCell<SocketStack>, | ||
| 66 | inner: RefCell<Inner<D>>, | ||
| 67 | } | ||
| 68 | |||
| 69 | struct Inner<D: Device> { | ||
| 70 | device: DeviceAdapter<D>, | ||
| 71 | link_up: bool, | ||
| 72 | config: Option<Config>, | ||
| 73 | #[cfg(feature = "dhcpv4")] | ||
| 74 | dhcp_socket: Option<SocketHandle>, | ||
| 75 | } | ||
| 76 | |||
| 77 | pub(crate) struct SocketStack { | ||
| 78 | pub(crate) sockets: SocketSet<'static>, | ||
| 79 | pub(crate) iface: Interface<'static>, | ||
| 80 | pub(crate) waker: WakerRegistration, | ||
| 81 | next_local_port: u16, | ||
| 82 | } | ||
| 83 | |||
| 84 | impl<D: Device + 'static> Stack<D> { | ||
| 85 | pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>( | ||
| 86 | device: D, | ||
| 87 | config: ConfigStrategy, | ||
| 88 | resources: &'static mut StackResources<ADDR, SOCK, NEIGH>, | ||
| 89 | random_seed: u64, | ||
| 90 | ) -> Self { | ||
| 91 | #[cfg(feature = "medium-ethernet")] | ||
| 92 | let medium = device.capabilities().medium; | ||
| 93 | |||
| 94 | #[cfg(feature = "medium-ethernet")] | ||
| 95 | let ethernet_addr = if medium == Medium::Ethernet { | ||
| 96 | device.ethernet_address() | ||
| 97 | } else { | ||
| 98 | [0, 0, 0, 0, 0, 0] | ||
| 99 | }; | ||
| 100 | |||
| 101 | let mut device = DeviceAdapter::new(device); | ||
| 102 | |||
| 103 | let mut b = InterfaceBuilder::new(); | ||
| 104 | b = b.ip_addrs(&mut resources.addresses[..]); | ||
| 105 | b = b.random_seed(random_seed); | ||
| 106 | |||
| 107 | #[cfg(feature = "medium-ethernet")] | ||
| 108 | if medium == Medium::Ethernet { | ||
| 109 | b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr))); | ||
| 110 | b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); | ||
| 111 | b = b.routes(Routes::new(&mut resources.routes[..])); | ||
| 112 | } | ||
| 113 | |||
| 114 | let iface = b.finalize(&mut device); | ||
| 115 | |||
| 116 | let sockets = SocketSet::new(&mut resources.sockets[..]); | ||
| 117 | |||
| 118 | let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; | ||
| 119 | |||
| 120 | let mut inner = Inner { | ||
| 121 | device, | ||
| 122 | link_up: false, | ||
| 123 | config: None, | ||
| 124 | #[cfg(feature = "dhcpv4")] | ||
| 125 | dhcp_socket: None, | ||
| 126 | }; | ||
| 127 | let mut socket = SocketStack { | ||
| 128 | sockets, | ||
| 129 | iface, | ||
| 130 | waker: WakerRegistration::new(), | ||
| 131 | next_local_port, | ||
| 132 | }; | ||
| 133 | |||
| 134 | match config { | ||
| 135 | ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config), | ||
| 136 | #[cfg(feature = "dhcpv4")] | ||
| 137 | ConfigStrategy::Dhcp => { | ||
| 138 | let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new()); | ||
| 139 | inner.dhcp_socket = Some(handle); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | Self { | ||
| 144 | socket: RefCell::new(socket), | ||
| 145 | inner: RefCell::new(inner), | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | fn with<R>(&self, f: impl FnOnce(&SocketStack, &Inner<D>) -> R) -> R { | ||
| 150 | f(&*self.socket.borrow(), &*self.inner.borrow()) | ||
| 151 | } | ||
| 152 | |||
| 153 | fn with_mut<R>(&self, f: impl FnOnce(&mut SocketStack, &mut Inner<D>) -> R) -> R { | ||
| 154 | f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) | ||
| 155 | } | ||
| 156 | |||
| 157 | pub fn ethernet_address(&self) -> [u8; 6] { | ||
| 158 | self.with(|_s, i| i.device.device.ethernet_address()) | ||
| 159 | } | ||
| 160 | |||
| 161 | pub fn is_link_up(&self) -> bool { | ||
| 162 | self.with(|_s, i| i.link_up) | ||
| 163 | } | ||
| 164 | |||
| 165 | pub fn is_config_up(&self) -> bool { | ||
| 166 | self.with(|_s, i| i.config.is_some()) | ||
| 167 | } | ||
| 168 | |||
| 169 | pub fn config(&self) -> Option<Config> { | ||
| 170 | self.with(|_s, i| i.config.clone()) | ||
| 171 | } | ||
| 172 | |||
| 173 | pub async fn run(&self) -> ! { | ||
| 174 | poll_fn(|cx| { | ||
| 175 | self.with_mut(|s, i| i.poll(cx, s)); | ||
| 176 | Poll::<()>::Pending | ||
| 177 | }) | ||
| 178 | .await; | ||
| 179 | unreachable!() | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | impl SocketStack { | ||
| 184 | #[allow(clippy::absurd_extreme_comparisons)] | ||
| 185 | pub fn get_local_port(&mut self) -> u16 { | ||
| 186 | let res = self.next_local_port; | ||
| 187 | self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; | ||
| 188 | res | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | impl<D: Device + 'static> Inner<D> { | ||
| 193 | fn apply_config(&mut self, s: &mut SocketStack, config: Config) { | ||
| 194 | #[cfg(feature = "medium-ethernet")] | ||
| 195 | let medium = self.device.capabilities().medium; | ||
| 196 | |||
| 197 | debug!("Acquired IP configuration:"); | ||
| 198 | |||
| 199 | debug!(" IP address: {}", config.address); | ||
| 200 | self.set_ipv4_addr(s, config.address); | ||
| 201 | |||
| 202 | #[cfg(feature = "medium-ethernet")] | ||
| 203 | if medium == Medium::Ethernet { | ||
| 204 | if let Some(gateway) = config.gateway { | ||
| 205 | debug!(" Default gateway: {}", gateway); | ||
| 206 | s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap(); | ||
| 207 | } else { | ||
| 208 | debug!(" Default gateway: None"); | ||
| 209 | s.iface.routes_mut().remove_default_ipv4_route(); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | for (i, s) in config.dns_servers.iter().enumerate() { | ||
| 213 | debug!(" DNS server {}: {}", i, s); | ||
| 214 | } | ||
| 215 | |||
| 216 | self.config = Some(config) | ||
| 217 | } | ||
| 218 | |||
| 219 | #[allow(unused)] // used only with dhcp | ||
| 220 | fn unapply_config(&mut self, s: &mut SocketStack) { | ||
| 221 | #[cfg(feature = "medium-ethernet")] | ||
| 222 | let medium = self.device.capabilities().medium; | ||
| 223 | |||
| 224 | debug!("Lost IP configuration"); | ||
| 225 | self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); | ||
| 226 | #[cfg(feature = "medium-ethernet")] | ||
| 227 | if medium == Medium::Ethernet { | ||
| 228 | s.iface.routes_mut().remove_default_ipv4_route(); | ||
| 229 | } | ||
| 230 | self.config = None | ||
| 231 | } | ||
| 232 | |||
| 233 | fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) { | ||
| 234 | s.iface.update_ip_addrs(|addrs| { | ||
| 235 | let dest = addrs.iter_mut().next().unwrap(); | ||
| 236 | *dest = IpCidr::Ipv4(cidr); | ||
| 237 | }); | ||
| 238 | } | ||
| 239 | |||
| 240 | fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { | ||
| 241 | self.device.device.register_waker(cx.waker()); | ||
| 242 | s.waker.register(cx.waker()); | ||
| 243 | |||
| 244 | let timestamp = instant_to_smoltcp(Instant::now()); | ||
| 245 | if s.iface.poll(timestamp, &mut self.device, &mut s.sockets).is_err() { | ||
| 246 | // If poll() returns error, it may not be done yet, so poll again later. | ||
| 247 | cx.waker().wake_by_ref(); | ||
| 248 | return; | ||
| 249 | } | ||
| 250 | |||
| 251 | // Update link up | ||
| 252 | let old_link_up = self.link_up; | ||
| 253 | self.link_up = self.device.device.link_state() == LinkState::Up; | ||
| 254 | |||
| 255 | // Print when changed | ||
| 256 | if old_link_up != self.link_up { | ||
| 257 | info!("link_up = {:?}", self.link_up); | ||
| 258 | } | ||
| 259 | |||
| 260 | #[cfg(feature = "dhcpv4")] | ||
| 261 | if let Some(dhcp_handle) = self.dhcp_socket { | ||
| 262 | let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle); | ||
| 263 | |||
| 264 | if self.link_up { | ||
| 265 | match socket.poll() { | ||
| 266 | None => {} | ||
| 267 | Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), | ||
| 268 | Some(dhcpv4::Event::Configured(config)) => { | ||
| 269 | let config = Config { | ||
| 270 | address: config.address, | ||
| 271 | gateway: config.router, | ||
| 272 | dns_servers: config.dns_servers, | ||
| 273 | }; | ||
| 274 | self.apply_config(s, config) | ||
| 275 | } | ||
| 276 | } | ||
| 277 | } else if old_link_up { | ||
| 278 | socket.reset(); | ||
| 279 | self.unapply_config(s); | ||
| 280 | } | ||
| 281 | } | ||
| 282 | //if old_link_up || self.link_up { | ||
| 283 | // self.poll_configurator(timestamp) | ||
| 284 | //} | ||
| 285 | |||
| 286 | if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { | ||
| 287 | let t = Timer::at(instant_from_smoltcp(poll_at)); | ||
| 288 | pin_mut!(t); | ||
| 289 | if t.poll(cx).is_ready() { | ||
| 290 | cx.waker().wake_by_ref(); | ||
| 291 | } | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | fn instant_to_smoltcp(instant: Instant) -> SmolInstant { | ||
| 297 | SmolInstant::from_millis(instant.as_millis() as i64) | ||
| 298 | } | ||
| 299 | |||
| 300 | fn instant_from_smoltcp(instant: SmolInstant) -> Instant { | ||
| 301 | Instant::from_millis(instant.total_millis() as u64) | ||
| 302 | } | ||
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 73cf2d4e4..0dc8da73a 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs | |||
| @@ -8,9 +8,8 @@ use smoltcp::socket::tcp; | |||
| 8 | use smoltcp::time::Duration; | 8 | use smoltcp::time::Duration; |
| 9 | use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; | 9 | use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; |
| 10 | 10 | ||
| 11 | use super::stack::Stack; | 11 | use crate::device::Device; |
| 12 | use crate::stack::SocketStack; | 12 | use crate::{SocketStack, Stack}; |
| 13 | use crate::Device; | ||
| 14 | 13 | ||
| 15 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | 14 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
| 16 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 15 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 4ddad77d4..2f5334df3 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs | |||
| @@ -7,8 +7,7 @@ use smoltcp::iface::{Interface, SocketHandle}; | |||
| 7 | use smoltcp::socket::udp::{self, PacketMetadata}; | 7 | use smoltcp::socket::udp::{self, PacketMetadata}; |
| 8 | use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; | 8 | use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; |
| 9 | 9 | ||
| 10 | use super::stack::SocketStack; | 10 | use crate::{Device, SocketStack, Stack}; |
| 11 | use crate::{Device, Stack}; | ||
| 12 | 11 | ||
| 13 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | 12 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
| 14 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b7f718c5f..0c491ee46 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -77,7 +77,6 @@ stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features | |||
| 77 | [features] | 77 | [features] |
| 78 | defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] | 78 | defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] |
| 79 | sdmmc-rs = ["embedded-sdmmc"] | 79 | sdmmc-rs = ["embedded-sdmmc"] |
| 80 | net = ["embassy-net" ] | ||
| 81 | memory-x = ["stm32-metapac/memory-x"] | 80 | memory-x = ["stm32-metapac/memory-x"] |
| 82 | subghz = [] | 81 | subghz = [] |
| 83 | exti = [] | 82 | exti = [] |
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 76a3dfab4..fd1b48530 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs | |||
| @@ -1,14 +1,131 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | #![cfg_attr(not(feature = "embassy-net"), allow(unused))] | ||
| 2 | 3 | ||
| 3 | #[cfg(feature = "net")] | ||
| 4 | #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] | 4 | #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] |
| 5 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] | 5 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] |
| 6 | mod _version; | 6 | mod _version; |
| 7 | pub mod generic_smi; | 7 | pub mod generic_smi; |
| 8 | 8 | ||
| 9 | #[cfg(feature = "net")] | ||
| 10 | pub use _version::*; | 9 | pub use _version::*; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 11 | 11 | ||
| 12 | #[allow(unused)] | ||
| 13 | const MTU: usize = 1514; | ||
| 14 | const TX_BUFFER_SIZE: usize = 1514; | ||
| 15 | const RX_BUFFER_SIZE: usize = 1536; | ||
| 16 | |||
| 17 | #[repr(C, align(8))] | ||
| 18 | #[derive(Copy, Clone)] | ||
| 19 | pub(crate) struct Packet<const N: usize>([u8; N]); | ||
| 20 | |||
| 21 | pub struct PacketQueue<const TX: usize, const RX: usize> { | ||
| 22 | tx_desc: [TDes; TX], | ||
| 23 | rx_desc: [RDes; RX], | ||
| 24 | tx_buf: [Packet<TX_BUFFER_SIZE>; TX], | ||
| 25 | rx_buf: [Packet<RX_BUFFER_SIZE>; RX], | ||
| 26 | } | ||
| 27 | |||
| 28 | impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> { | ||
| 29 | pub const fn new() -> Self { | ||
| 30 | const NEW_TDES: TDes = TDes::new(); | ||
| 31 | const NEW_RDES: RDes = RDes::new(); | ||
| 32 | Self { | ||
| 33 | tx_desc: [NEW_TDES; TX], | ||
| 34 | rx_desc: [NEW_RDES; RX], | ||
| 35 | tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], | ||
| 36 | rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], | ||
| 37 | } | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 42 | |||
| 43 | #[cfg(feature = "embassy-net")] | ||
| 44 | mod embassy_net_impl { | ||
| 45 | use core::task::Context; | ||
| 46 | |||
| 47 | use embassy_net::device::{Device, DeviceCapabilities, LinkState}; | ||
| 48 | |||
| 49 | use super::*; | ||
| 50 | |||
| 51 | impl<'d, T: Instance, P: PHY> Device for Ethernet<'d, T, P> { | ||
| 52 | type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; | ||
| 53 | type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; | ||
| 54 | |||
| 55 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { | ||
| 56 | WAKER.register(cx.waker()); | ||
| 57 | if self.rx.available().is_some() && self.tx.available().is_some() { | ||
| 58 | Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) | ||
| 59 | } else { | ||
| 60 | None | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> { | ||
| 65 | WAKER.register(cx.waker()); | ||
| 66 | if self.tx.available().is_some() { | ||
| 67 | Some(TxToken { tx: &mut self.tx }) | ||
| 68 | } else { | ||
| 69 | None | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | fn capabilities(&self) -> DeviceCapabilities { | ||
| 74 | let mut caps = DeviceCapabilities::default(); | ||
| 75 | caps.max_transmission_unit = MTU; | ||
| 76 | caps.max_burst_size = Some(self.tx.len()); | ||
| 77 | caps | ||
| 78 | } | ||
| 79 | |||
| 80 | fn link_state(&mut self, cx: &mut Context) -> LinkState { | ||
| 81 | // TODO: wake cx.waker on link state change | ||
| 82 | cx.waker().wake_by_ref(); | ||
| 83 | if P::poll_link(self) { | ||
| 84 | LinkState::Up | ||
| 85 | } else { | ||
| 86 | LinkState::Down | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 91 | self.mac_addr | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | pub struct RxToken<'a, 'd> { | ||
| 96 | rx: &'a mut RDesRing<'d>, | ||
| 97 | } | ||
| 98 | |||
| 99 | impl<'a, 'd> embassy_net::device::RxToken for RxToken<'a, 'd> { | ||
| 100 | fn consume<R, F>(self, f: F) -> R | ||
| 101 | where | ||
| 102 | F: FnOnce(&mut [u8]) -> R, | ||
| 103 | { | ||
| 104 | // NOTE(unwrap): we checked the queue wasn't full when creating the token. | ||
| 105 | let pkt = unwrap!(self.rx.available()); | ||
| 106 | let r = f(pkt); | ||
| 107 | self.rx.pop_packet(); | ||
| 108 | r | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | pub struct TxToken<'a, 'd> { | ||
| 113 | tx: &'a mut TDesRing<'d>, | ||
| 114 | } | ||
| 115 | |||
| 116 | impl<'a, 'd> embassy_net::device::TxToken for TxToken<'a, 'd> { | ||
| 117 | fn consume<R, F>(self, len: usize, f: F) -> R | ||
| 118 | where | ||
| 119 | F: FnOnce(&mut [u8]) -> R, | ||
| 120 | { | ||
| 121 | // NOTE(unwrap): we checked the queue wasn't full when creating the token. | ||
| 122 | let pkt = unwrap!(self.tx.available()); | ||
| 123 | let r = f(&mut pkt[..len]); | ||
| 124 | self.tx.transmit(len); | ||
| 125 | r | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 12 | /// Station Management Interface (SMI) on an ethernet PHY | 129 | /// Station Management Interface (SMI) on an ethernet PHY |
| 13 | /// | 130 | /// |
| 14 | /// # Safety | 131 | /// # Safety |
diff --git a/embassy-stm32/src/eth/v1/descriptors.rs b/embassy-stm32/src/eth/v1/descriptors.rs deleted file mode 100644 index 25f21ce19..000000000 --- a/embassy-stm32/src/eth/v1/descriptors.rs +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | use crate::eth::_version::rx_desc::RDesRing; | ||
| 2 | use crate::eth::_version::tx_desc::TDesRing; | ||
| 3 | |||
| 4 | pub struct DescriptorRing<const T: usize, const R: usize> { | ||
| 5 | pub(crate) tx: TDesRing<T>, | ||
| 6 | pub(crate) rx: RDesRing<R>, | ||
| 7 | } | ||
| 8 | |||
| 9 | impl<const T: usize, const R: usize> DescriptorRing<T, R> { | ||
| 10 | pub const fn new() -> Self { | ||
| 11 | Self { | ||
| 12 | tx: TDesRing::new(), | ||
| 13 | rx: RDesRing::new(), | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | pub fn init(&mut self) { | ||
| 18 | self.tx.init(); | ||
| 19 | self.rx.init(); | ||
| 20 | } | ||
| 21 | } | ||
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 38629a932..de36d3da1 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs | |||
| @@ -1,14 +1,17 @@ | |||
| 1 | // The v1c ethernet driver was ported to embassy from the awesome stm32-eth project (https://github.com/stm32-rs/stm32-eth). | 1 | // The v1c ethernet driver was ported to embassy from the awesome stm32-eth project (https://github.com/stm32-rs/stm32-eth). |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | mod rx_desc; |
| 4 | mod tx_desc; | ||
| 5 | |||
| 4 | use core::sync::atomic::{fence, Ordering}; | 6 | use core::sync::atomic::{fence, Ordering}; |
| 5 | use core::task::Waker; | ||
| 6 | 7 | ||
| 7 | use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; | 8 | use embassy_cortex_m::interrupt::InterruptExt; |
| 8 | use embassy_hal_common::{into_ref, PeripheralRef}; | 9 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 9 | use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU}; | 10 | use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 11 | 11 | ||
| 12 | pub(crate) use self::rx_desc::{RDes, RDesRing}; | ||
| 13 | pub(crate) use self::tx_desc::{TDes, TDesRing}; | ||
| 14 | use super::*; | ||
| 12 | use crate::gpio::sealed::{AFType, Pin as __GpioPin}; | 15 | use crate::gpio::sealed::{AFType, Pin as __GpioPin}; |
| 13 | use crate::gpio::{AnyPin, Speed}; | 16 | use crate::gpio::{AnyPin, Speed}; |
| 14 | #[cfg(eth_v1a)] | 17 | #[cfg(eth_v1a)] |
| @@ -18,29 +21,16 @@ use crate::pac::SYSCFG; | |||
| 18 | use crate::pac::{ETH, RCC}; | 21 | use crate::pac::{ETH, RCC}; |
| 19 | use crate::Peripheral; | 22 | use crate::Peripheral; |
| 20 | 23 | ||
| 21 | mod descriptors; | 24 | pub struct Ethernet<'d, T: Instance, P: PHY> { |
| 22 | mod rx_desc; | 25 | _peri: PeripheralRef<'d, T>, |
| 23 | mod tx_desc; | 26 | pub(crate) tx: TDesRing<'d>, |
| 27 | pub(crate) rx: RDesRing<'d>, | ||
| 24 | 28 | ||
| 25 | use descriptors::DescriptorRing; | ||
| 26 | use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; | ||
| 27 | |||
| 28 | use super::*; | ||
| 29 | |||
| 30 | pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage<Inner<'d, T, TX, RX>>); | ||
| 31 | impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { | ||
| 32 | pub const fn new() -> Self { | ||
| 33 | Self(StateStorage::new()) | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> { | ||
| 38 | state: PeripheralMutex<'d, Inner<'d, T, TX, RX>>, | ||
| 39 | pins: [PeripheralRef<'d, AnyPin>; 9], | 29 | pins: [PeripheralRef<'d, AnyPin>; 9], |
| 40 | _phy: P, | 30 | _phy: P, |
| 41 | clock_range: Cr, | 31 | clock_range: Cr, |
| 42 | phy_addr: u8, | 32 | phy_addr: u8, |
| 43 | mac_addr: [u8; 6], | 33 | pub(crate) mac_addr: [u8; 6], |
| 44 | } | 34 | } |
| 45 | 35 | ||
| 46 | #[cfg(eth_v1a)] | 36 | #[cfg(eth_v1a)] |
| @@ -82,10 +72,10 @@ macro_rules! config_pins { | |||
| 82 | }; | 72 | }; |
| 83 | } | 73 | } |
| 84 | 74 | ||
| 85 | impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> { | 75 | impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { |
| 86 | /// safety: the returned instance is not leak-safe | 76 | /// safety: the returned instance is not leak-safe |
| 87 | pub unsafe fn new( | 77 | pub fn new<const TX: usize, const RX: usize>( |
| 88 | state: &'d mut State<'d, T, TX, RX>, | 78 | queue: &'d mut PacketQueue<TX, RX>, |
| 89 | peri: impl Peripheral<P = T> + 'd, | 79 | peri: impl Peripheral<P = T> + 'd, |
| 90 | interrupt: impl Peripheral<P = crate::interrupt::ETH> + 'd, | 80 | interrupt: impl Peripheral<P = crate::interrupt::ETH> + 'd, |
| 91 | ref_clk: impl Peripheral<P = impl RefClkPin<T>> + 'd, | 81 | ref_clk: impl Peripheral<P = impl RefClkPin<T>> + 'd, |
| @@ -101,134 +91,131 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, | |||
| 101 | mac_addr: [u8; 6], | 91 | mac_addr: [u8; 6], |
| 102 | phy_addr: u8, | 92 | phy_addr: u8, |
| 103 | ) -> Self { | 93 | ) -> Self { |
| 104 | into_ref!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 94 | into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); |
| 105 | 95 | ||
| 106 | // Enable the necessary Clocks | 96 | unsafe { |
| 107 | // NOTE(unsafe) We have exclusive access to the registers | 97 | // Enable the necessary Clocks |
| 108 | #[cfg(eth_v1a)] | 98 | // NOTE(unsafe) We have exclusive access to the registers |
| 109 | critical_section::with(|_| { | 99 | #[cfg(eth_v1a)] |
| 110 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | 100 | critical_section::with(|_| { |
| 101 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 102 | |||
| 103 | // Select RMII (Reduced Media Independent Interface) | ||
| 104 | // Must be done prior to enabling peripheral clock | ||
| 105 | AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); | ||
| 106 | |||
| 107 | RCC.ahbenr().modify(|w| { | ||
| 108 | w.set_ethen(true); | ||
| 109 | w.set_ethtxen(true); | ||
| 110 | w.set_ethrxen(true); | ||
| 111 | }); | ||
| 112 | }); | ||
| 113 | |||
| 114 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 115 | critical_section::with(|_| { | ||
| 116 | RCC.apb2enr().modify(|w| w.set_syscfgen(true)); | ||
| 117 | RCC.ahb1enr().modify(|w| { | ||
| 118 | w.set_ethen(true); | ||
| 119 | w.set_ethtxen(true); | ||
| 120 | w.set_ethrxen(true); | ||
| 121 | }); | ||
| 122 | |||
| 123 | // RMII (Reduced Media Independent Interface) | ||
| 124 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); | ||
| 125 | }); | ||
| 126 | |||
| 127 | #[cfg(eth_v1a)] | ||
| 128 | { | ||
| 129 | config_in_pins!(ref_clk, rx_d0, rx_d1); | ||
| 130 | config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); | ||
| 131 | } | ||
| 132 | |||
| 133 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 134 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | ||
| 135 | |||
| 136 | // NOTE(unsafe) We have exclusive access to the registers | ||
| 137 | let dma = ETH.ethernet_dma(); | ||
| 138 | let mac = ETH.ethernet_mac(); | ||
| 111 | 139 | ||
| 112 | // Select RMII (Reduced Media Independent Interface) | 140 | // Reset and wait |
| 113 | // Must be done prior to enabling peripheral clock | 141 | dma.dmabmr().modify(|w| w.set_sr(true)); |
| 114 | AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); | 142 | while dma.dmabmr().read().sr() {} |
| 115 | 143 | ||
| 116 | RCC.ahbenr().modify(|w| { | 144 | mac.maccr().modify(|w| { |
| 117 | w.set_ethen(true); | 145 | w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times |
| 118 | w.set_ethtxen(true); | 146 | w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping |
| 119 | w.set_ethrxen(true); | 147 | w.set_fes(Fes::FES100); // fast ethernet speed |
| 148 | w.set_dm(Dm::FULLDUPLEX); // full duplex | ||
| 149 | // TODO: Carrier sense ? ECRSFD | ||
| 120 | }); | 150 | }); |
| 121 | }); | ||
| 122 | 151 | ||
| 123 | #[cfg(any(eth_v1b, eth_v1c))] | 152 | // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, |
| 124 | critical_section::with(|_| { | 153 | // so the LR write must happen after the HR write. |
| 125 | RCC.apb2enr().modify(|w| w.set_syscfgen(true)); | 154 | mac.maca0hr() |
| 126 | RCC.ahb1enr().modify(|w| { | 155 | .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); |
| 127 | w.set_ethen(true); | 156 | mac.maca0lr().write(|w| { |
| 128 | w.set_ethtxen(true); | 157 | w.set_maca0l( |
| 129 | w.set_ethrxen(true); | 158 | u32::from(mac_addr[0]) |
| 159 | | (u32::from(mac_addr[1]) << 8) | ||
| 160 | | (u32::from(mac_addr[2]) << 16) | ||
| 161 | | (u32::from(mac_addr[3]) << 24), | ||
| 162 | ) | ||
| 130 | }); | 163 | }); |
| 131 | 164 | ||
| 132 | // RMII (Reduced Media Independent Interface) | 165 | // pause time |
| 133 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); | 166 | mac.macfcr().modify(|w| w.set_pt(0x100)); |
| 134 | }); | ||
| 135 | 167 | ||
| 136 | #[cfg(eth_v1a)] | 168 | // Transfer and Forward, Receive and Forward |
| 137 | { | 169 | dma.dmaomr().modify(|w| { |
| 138 | config_in_pins!(ref_clk, rx_d0, rx_d1); | 170 | w.set_tsf(Tsf::STOREFORWARD); |
| 139 | config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); | 171 | w.set_rsf(Rsf::STOREFORWARD); |
| 140 | } | 172 | }); |
| 141 | 173 | ||
| 142 | #[cfg(any(eth_v1b, eth_v1c))] | 174 | dma.dmabmr().modify(|w| { |
| 143 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 175 | w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ? |
| 144 | 176 | }); | |
| 145 | // NOTE(unsafe) We are ourselves not leak-safe. | 177 | |
| 146 | let state = PeripheralMutex::new(interrupt, &mut state.0, || Inner::new(peri)); | 178 | // TODO MTU size setting not found for v1 ethernet, check if correct |
| 147 | 179 | ||
| 148 | // NOTE(unsafe) We have exclusive access to the registers | 180 | // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called |
| 149 | let dma = ETH.ethernet_dma(); | 181 | let hclk = crate::rcc::get_freqs().ahb1; |
| 150 | let mac = ETH.ethernet_mac(); | 182 | let hclk_mhz = hclk.0 / 1_000_000; |
| 151 | 183 | ||
| 152 | // Reset and wait | 184 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz |
| 153 | dma.dmabmr().modify(|w| w.set_sr(true)); | 185 | let clock_range = match hclk_mhz { |
| 154 | while dma.dmabmr().read().sr() {} | 186 | 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), |
| 155 | 187 | 25..=34 => Cr::CR_20_35, // Divide by 16 | |
| 156 | mac.maccr().modify(|w| { | 188 | 35..=59 => Cr::CR_35_60, // Divide by 26 |
| 157 | w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times | 189 | 60..=99 => Cr::CR_60_100, // Divide by 42 |
| 158 | w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping | 190 | 100..=149 => Cr::CR_100_150, // Divide by 62 |
| 159 | w.set_fes(Fes::FES100); // fast ethernet speed | 191 | 150..=216 => Cr::CR_150_168, // Divide by 102 |
| 160 | w.set_dm(Dm::FULLDUPLEX); // full duplex | 192 | _ => { |
| 161 | // TODO: Carrier sense ? ECRSFD | 193 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") |
| 162 | }); | 194 | } |
| 163 | 195 | }; | |
| 164 | // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, | 196 | |
| 165 | // so the LR write must happen after the HR write. | 197 | let pins = [ |
| 166 | mac.maca0hr() | 198 | ref_clk.map_into(), |
| 167 | .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); | 199 | mdio.map_into(), |
| 168 | mac.maca0lr().write(|w| { | 200 | mdc.map_into(), |
| 169 | w.set_maca0l( | 201 | crs.map_into(), |
| 170 | u32::from(mac_addr[0]) | 202 | rx_d0.map_into(), |
| 171 | | (u32::from(mac_addr[1]) << 8) | 203 | rx_d1.map_into(), |
| 172 | | (u32::from(mac_addr[2]) << 16) | 204 | tx_d0.map_into(), |
| 173 | | (u32::from(mac_addr[3]) << 24), | 205 | tx_d1.map_into(), |
| 174 | ) | 206 | tx_en.map_into(), |
| 175 | }); | 207 | ]; |
| 176 | 208 | ||
| 177 | // pause time | 209 | let mut this = Self { |
| 178 | mac.macfcr().modify(|w| w.set_pt(0x100)); | 210 | _peri: peri, |
| 179 | 211 | pins, | |
| 180 | // Transfer and Forward, Receive and Forward | 212 | _phy: phy, |
| 181 | dma.dmaomr().modify(|w| { | 213 | clock_range, |
| 182 | w.set_tsf(Tsf::STOREFORWARD); | 214 | phy_addr, |
| 183 | w.set_rsf(Rsf::STOREFORWARD); | 215 | mac_addr, |
| 184 | }); | 216 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 185 | 217 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), | |
| 186 | dma.dmabmr().modify(|w| { | 218 | }; |
| 187 | w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ? | ||
| 188 | }); | ||
| 189 | |||
| 190 | // TODO MTU size setting not found for v1 ethernet, check if correct | ||
| 191 | |||
| 192 | // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called | ||
| 193 | let hclk = crate::rcc::get_freqs().ahb1; | ||
| 194 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 195 | |||
| 196 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 197 | let clock_range = match hclk_mhz { | ||
| 198 | 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), | ||
| 199 | 25..=34 => Cr::CR_20_35, // Divide by 16 | ||
| 200 | 35..=59 => Cr::CR_35_60, // Divide by 26 | ||
| 201 | 60..=99 => Cr::CR_60_100, // Divide by 42 | ||
| 202 | 100..=149 => Cr::CR_100_150, // Divide by 62 | ||
| 203 | 150..=216 => Cr::CR_150_168, // Divide by 102 | ||
| 204 | _ => { | ||
| 205 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 206 | } | ||
| 207 | }; | ||
| 208 | |||
| 209 | let pins = [ | ||
| 210 | ref_clk.map_into(), | ||
| 211 | mdio.map_into(), | ||
| 212 | mdc.map_into(), | ||
| 213 | crs.map_into(), | ||
| 214 | rx_d0.map_into(), | ||
| 215 | rx_d1.map_into(), | ||
| 216 | tx_d0.map_into(), | ||
| 217 | tx_d1.map_into(), | ||
| 218 | tx_en.map_into(), | ||
| 219 | ]; | ||
| 220 | |||
| 221 | let mut this = Self { | ||
| 222 | state, | ||
| 223 | pins, | ||
| 224 | _phy: phy, | ||
| 225 | clock_range, | ||
| 226 | phy_addr, | ||
| 227 | mac_addr, | ||
| 228 | }; | ||
| 229 | |||
| 230 | this.state.with(|s| { | ||
| 231 | s.desc_ring.init(); | ||
| 232 | 219 | ||
| 233 | fence(Ordering::SeqCst); | 220 | fence(Ordering::SeqCst); |
| 234 | 221 | ||
| @@ -245,23 +232,45 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, | |||
| 245 | w.set_sr(DmaomrSr::STARTED); // start receiving channel | 232 | w.set_sr(DmaomrSr::STARTED); // start receiving channel |
| 246 | }); | 233 | }); |
| 247 | 234 | ||
| 235 | this.rx.demand_poll(); | ||
| 236 | |||
| 248 | // Enable interrupts | 237 | // Enable interrupts |
| 249 | dma.dmaier().modify(|w| { | 238 | dma.dmaier().modify(|w| { |
| 250 | w.set_nise(true); | 239 | w.set_nise(true); |
| 251 | w.set_rie(true); | 240 | w.set_rie(true); |
| 252 | w.set_tie(true); | 241 | w.set_tie(true); |
| 253 | }); | 242 | }); |
| 254 | }); | ||
| 255 | P::phy_reset(&mut this); | ||
| 256 | P::phy_init(&mut this); | ||
| 257 | 243 | ||
| 258 | this | 244 | P::phy_reset(&mut this); |
| 245 | P::phy_init(&mut this); | ||
| 246 | |||
| 247 | interrupt.set_handler(Self::on_interrupt); | ||
| 248 | interrupt.enable(); | ||
| 249 | |||
| 250 | this | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | fn on_interrupt(_cx: *mut ()) { | ||
| 255 | WAKER.wake(); | ||
| 256 | |||
| 257 | // TODO: Check and clear more flags | ||
| 258 | unsafe { | ||
| 259 | let dma = ETH.ethernet_dma(); | ||
| 260 | |||
| 261 | dma.dmasr().modify(|w| { | ||
| 262 | w.set_ts(true); | ||
| 263 | w.set_rs(true); | ||
| 264 | w.set_nis(true); | ||
| 265 | }); | ||
| 266 | // Delay two peripheral's clock | ||
| 267 | dma.dmasr().read(); | ||
| 268 | dma.dmasr().read(); | ||
| 269 | } | ||
| 259 | } | 270 | } |
| 260 | } | 271 | } |
| 261 | 272 | ||
| 262 | unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement | 273 | unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { |
| 263 | for Ethernet<'d, T, P, TX, RX> | ||
| 264 | { | ||
| 265 | fn smi_read(&mut self, reg: u8) -> u16 { | 274 | fn smi_read(&mut self, reg: u8) -> u16 { |
| 266 | // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` | 275 | // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` |
| 267 | unsafe { | 276 | unsafe { |
| @@ -297,44 +306,7 @@ unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationMa | |||
| 297 | } | 306 | } |
| 298 | } | 307 | } |
| 299 | 308 | ||
| 300 | impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device for Ethernet<'d, T, P, TX, RX> { | 309 | impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { |
| 301 | fn is_transmit_ready(&mut self) -> bool { | ||
| 302 | self.state.with(|s| s.desc_ring.tx.available()) | ||
| 303 | } | ||
| 304 | |||
| 305 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 306 | self.state.with(|s| unwrap!(s.desc_ring.tx.transmit(pkt))); | ||
| 307 | } | ||
| 308 | |||
| 309 | fn receive(&mut self) -> Option<PacketBuf> { | ||
| 310 | self.state.with(|s| s.desc_ring.rx.pop_packet()) | ||
| 311 | } | ||
| 312 | |||
| 313 | fn register_waker(&mut self, waker: &Waker) { | ||
| 314 | WAKER.register(waker); | ||
| 315 | } | ||
| 316 | |||
| 317 | fn capabilities(&self) -> DeviceCapabilities { | ||
| 318 | let mut caps = DeviceCapabilities::default(); | ||
| 319 | caps.max_transmission_unit = MTU; | ||
| 320 | caps.max_burst_size = Some(TX.min(RX)); | ||
| 321 | caps | ||
| 322 | } | ||
| 323 | |||
| 324 | fn link_state(&mut self) -> LinkState { | ||
| 325 | if P::poll_link(self) { | ||
| 326 | LinkState::Up | ||
| 327 | } else { | ||
| 328 | LinkState::Down | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 333 | self.mac_addr | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, P, TX, RX> { | ||
| 338 | fn drop(&mut self) { | 310 | fn drop(&mut self) { |
| 339 | // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers | 311 | // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers |
| 340 | unsafe { | 312 | unsafe { |
| @@ -361,46 +333,3 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Etherne | |||
| 361 | }) | 333 | }) |
| 362 | } | 334 | } |
| 363 | } | 335 | } |
| 364 | |||
| 365 | //---------------------------------------------------------------------- | ||
| 366 | |||
| 367 | struct Inner<'d, T: Instance, const TX: usize, const RX: usize> { | ||
| 368 | _peri: PhantomData<&'d mut T>, | ||
| 369 | desc_ring: DescriptorRing<TX, RX>, | ||
| 370 | } | ||
| 371 | |||
| 372 | impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> { | ||
| 373 | pub fn new(_peri: impl Peripheral<P = T> + 'd) -> Self { | ||
| 374 | Self { | ||
| 375 | _peri: PhantomData, | ||
| 376 | desc_ring: DescriptorRing::new(), | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> { | ||
| 382 | type Interrupt = crate::interrupt::ETH; | ||
| 383 | |||
| 384 | fn on_interrupt(&mut self) { | ||
| 385 | unwrap!(self.desc_ring.tx.on_interrupt()); | ||
| 386 | self.desc_ring.rx.on_interrupt(); | ||
| 387 | |||
| 388 | WAKER.wake(); | ||
| 389 | |||
| 390 | // TODO: Check and clear more flags | ||
| 391 | unsafe { | ||
| 392 | let dma = ETH.ethernet_dma(); | ||
| 393 | |||
| 394 | dma.dmasr().modify(|w| { | ||
| 395 | w.set_ts(true); | ||
| 396 | w.set_rs(true); | ||
| 397 | w.set_nis(true); | ||
| 398 | }); | ||
| 399 | // Delay two peripheral's clock | ||
| 400 | dma.dmasr().read(); | ||
| 401 | dma.dmasr().read(); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs index d482590a1..8b8566d92 100644 --- a/embassy-stm32/src/eth/v1/rx_desc.rs +++ b/embassy-stm32/src/eth/v1/rx_desc.rs | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | use core::sync::atomic::{compiler_fence, fence, Ordering}; | 1 | use core::sync::atomic::{compiler_fence, fence, Ordering}; |
| 2 | 2 | ||
| 3 | use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf}; | 3 | use stm32_metapac::eth::vals::{Rpd, Rps}; |
| 4 | use stm32_metapac::eth::vals::{DmaomrSr, Rpd, Rps}; | ||
| 5 | use vcell::VolatileCell; | 4 | use vcell::VolatileCell; |
| 6 | 5 | ||
| 6 | use crate::eth::RX_BUFFER_SIZE; | ||
| 7 | use crate::pac::ETH; | 7 | use crate::pac::ETH; |
| 8 | 8 | ||
| 9 | mod rx_consts { | 9 | mod rx_consts { |
| @@ -28,6 +28,8 @@ mod rx_consts { | |||
| 28 | 28 | ||
| 29 | use rx_consts::*; | 29 | use rx_consts::*; |
| 30 | 30 | ||
| 31 | use super::Packet; | ||
| 32 | |||
| 31 | /// Receive Descriptor representation | 33 | /// Receive Descriptor representation |
| 32 | /// | 34 | /// |
| 33 | /// * rdes0: OWN and Status | 35 | /// * rdes0: OWN and Status |
| @@ -35,7 +37,7 @@ use rx_consts::*; | |||
| 35 | /// * rdes2: data buffer address | 37 | /// * rdes2: data buffer address |
| 36 | /// * rdes3: next descriptor address | 38 | /// * rdes3: next descriptor address |
| 37 | #[repr(C)] | 39 | #[repr(C)] |
| 38 | struct RDes { | 40 | pub(crate) struct RDes { |
| 39 | rdes0: VolatileCell<u32>, | 41 | rdes0: VolatileCell<u32>, |
| 40 | rdes1: VolatileCell<u32>, | 42 | rdes1: VolatileCell<u32>, |
| 41 | rdes2: VolatileCell<u32>, | 43 | rdes2: VolatileCell<u32>, |
| @@ -54,7 +56,7 @@ impl RDes { | |||
| 54 | 56 | ||
| 55 | /// Return true if this RDes is acceptable to us | 57 | /// Return true if this RDes is acceptable to us |
| 56 | #[inline(always)] | 58 | #[inline(always)] |
| 57 | pub fn valid(&self) -> bool { | 59 | fn valid(&self) -> bool { |
| 58 | // Write-back descriptor is valid if: | 60 | // Write-back descriptor is valid if: |
| 59 | // | 61 | // |
| 60 | // Contains first buffer of packet AND contains last buf of | 62 | // Contains first buffer of packet AND contains last buf of |
| @@ -64,15 +66,16 @@ impl RDes { | |||
| 64 | 66 | ||
| 65 | /// Return true if this RDes is not currently owned by the DMA | 67 | /// Return true if this RDes is not currently owned by the DMA |
| 66 | #[inline(always)] | 68 | #[inline(always)] |
| 67 | pub fn available(&self) -> bool { | 69 | fn available(&self) -> bool { |
| 68 | self.rdes0.get() & RXDESC_0_OWN == 0 // Owned by us | 70 | self.rdes0.get() & RXDESC_0_OWN == 0 // Owned by us |
| 69 | } | 71 | } |
| 70 | 72 | ||
| 71 | /// Configures the reception buffer address and length and passed descriptor ownership to the DMA | 73 | /// Configures the reception buffer address and length and passed descriptor ownership to the DMA |
| 72 | #[inline(always)] | 74 | #[inline(always)] |
| 73 | pub fn set_ready(&mut self, buf_addr: u32, buf_len: usize) { | 75 | fn set_ready(&self, buf: *mut u8) { |
| 74 | self.rdes1.set(self.rdes1.get() | (buf_len as u32) & RXDESC_1_RBS_MASK); | 76 | self.rdes1 |
| 75 | self.rdes2.set(buf_addr); | 77 | .set(self.rdes1.get() | (RX_BUFFER_SIZE as u32) & RXDESC_1_RBS_MASK); |
| 78 | self.rdes2.set(buf as u32); | ||
| 76 | 79 | ||
| 77 | // "Preceding reads and writes cannot be moved past subsequent writes." | 80 | // "Preceding reads and writes cannot be moved past subsequent writes." |
| 78 | fence(Ordering::Release); | 81 | fence(Ordering::Release); |
| @@ -88,12 +91,12 @@ impl RDes { | |||
| 88 | 91 | ||
| 89 | // points to next descriptor (RCH) | 92 | // points to next descriptor (RCH) |
| 90 | #[inline(always)] | 93 | #[inline(always)] |
| 91 | fn set_buffer2(&mut self, buffer: *const u8) { | 94 | fn set_buffer2(&self, buffer: *const u8) { |
| 92 | self.rdes3.set(buffer as u32); | 95 | self.rdes3.set(buffer as u32); |
| 93 | } | 96 | } |
| 94 | 97 | ||
| 95 | #[inline(always)] | 98 | #[inline(always)] |
| 96 | fn set_end_of_ring(&mut self) { | 99 | fn set_end_of_ring(&self) { |
| 97 | self.rdes1.set(self.rdes1.get() | RXDESC_1_RER); | 100 | self.rdes1.set(self.rdes1.get() | RXDESC_1_RER); |
| 98 | } | 101 | } |
| 99 | 102 | ||
| @@ -102,7 +105,7 @@ impl RDes { | |||
| 102 | ((self.rdes0.get() >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize | 105 | ((self.rdes0.get() >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize |
| 103 | } | 106 | } |
| 104 | 107 | ||
| 105 | pub fn setup(&mut self, next: Option<&Self>) { | 108 | fn setup(&self, next: Option<&Self>, buf: *mut u8) { |
| 106 | // Defer this initialization to this function, so we can have `RingEntry` on bss. | 109 | // Defer this initialization to this function, so we can have `RingEntry` on bss. |
| 107 | self.rdes1.set(self.rdes1.get() | RXDESC_1_RCH); | 110 | self.rdes1.set(self.rdes1.get() | RXDESC_1_RCH); |
| 108 | 111 | ||
| @@ -113,8 +116,11 @@ impl RDes { | |||
| 113 | self.set_end_of_ring(); | 116 | self.set_end_of_ring(); |
| 114 | } | 117 | } |
| 115 | } | 118 | } |
| 119 | |||
| 120 | self.set_ready(buf); | ||
| 116 | } | 121 | } |
| 117 | } | 122 | } |
| 123 | |||
| 118 | /// Running state of the `RxRing` | 124 | /// Running state of the `RxRing` |
| 119 | #[derive(PartialEq, Eq, Debug)] | 125 | #[derive(PartialEq, Eq, Debug)] |
| 120 | pub enum RunningState { | 126 | pub enum RunningState { |
| @@ -123,116 +129,42 @@ pub enum RunningState { | |||
| 123 | Running, | 129 | Running, |
| 124 | } | 130 | } |
| 125 | 131 | ||
| 126 | impl RunningState { | ||
| 127 | /// whether self equals to `RunningState::Running` | ||
| 128 | pub fn is_running(&self) -> bool { | ||
| 129 | *self == RunningState::Running | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Rx ring of descriptors and packets | 132 | /// Rx ring of descriptors and packets |
| 134 | /// | 133 | pub(crate) struct RDesRing<'a> { |
| 135 | /// This ring has three major locations that work in lock-step. The DMA will never write to the tail | 134 | descriptors: &'a mut [RDes], |
| 136 | /// index, so the `read_index` must never pass the tail index. The `next_tail_index` is always 1 | 135 | buffers: &'a mut [Packet<RX_BUFFER_SIZE>], |
| 137 | /// slot ahead of the real tail index, and it must never pass the `read_index` or it could overwrite | 136 | index: usize, |
| 138 | /// a packet still to be passed to the application. | ||
| 139 | /// | ||
| 140 | /// nt can't pass r (no alloc) | ||
| 141 | /// +---+---+---+---+ Read ok +---+---+---+---+ No Read +---+---+---+---+ | ||
| 142 | /// | | | | | ------------> | | | | | ------------> | | | | | | ||
| 143 | /// +---+---+---+---+ Allocation ok +---+---+---+---+ +---+---+---+---+ | ||
| 144 | /// ^ ^t ^t ^ ^t ^ | ||
| 145 | /// |r |r |r | ||
| 146 | /// |nt |nt |nt | ||
| 147 | /// | ||
| 148 | /// | ||
| 149 | /// +---+---+---+---+ Read ok +---+---+---+---+ Can't read +---+---+---+---+ | ||
| 150 | /// | | | | | ------------> | | | | | ------------> | | | | | | ||
| 151 | /// +---+---+---+---+ Allocation fail +---+---+---+---+ Allocation ok +---+---+---+---+ | ||
| 152 | /// ^ ^t ^ ^t ^ ^ ^ ^t | ||
| 153 | /// |r | |r | | |r | ||
| 154 | /// |nt |nt |nt | ||
| 155 | /// | ||
| 156 | pub(crate) struct RDesRing<const N: usize> { | ||
| 157 | descriptors: [RDes; N], | ||
| 158 | buffers: [Option<PacketBox>; N], | ||
| 159 | read_index: usize, | ||
| 160 | next_tail_index: usize, | ||
| 161 | } | 137 | } |
| 162 | 138 | ||
| 163 | impl<const N: usize> RDesRing<N> { | 139 | impl<'a> RDesRing<'a> { |
| 164 | pub const fn new() -> Self { | 140 | pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet<RX_BUFFER_SIZE>]) -> Self { |
| 165 | const RDES: RDes = RDes::new(); | 141 | assert!(descriptors.len() > 1); |
| 166 | const BUFFERS: Option<PacketBox> = None; | 142 | assert!(descriptors.len() == buffers.len()); |
| 167 | |||
| 168 | Self { | ||
| 169 | descriptors: [RDES; N], | ||
| 170 | buffers: [BUFFERS; N], | ||
| 171 | read_index: 0, | ||
| 172 | next_tail_index: 0, | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | pub(crate) fn init(&mut self) { | ||
| 177 | assert!(N > 1); | ||
| 178 | let mut last_index = 0; | ||
| 179 | for (index, buf) in self.buffers.iter_mut().enumerate() { | ||
| 180 | let pkt = match PacketBox::new(Packet::new()) { | ||
| 181 | Some(p) => p, | ||
| 182 | None => { | ||
| 183 | if index == 0 { | ||
| 184 | panic!("Could not allocate at least one buffer for Ethernet receiving"); | ||
| 185 | } else { | ||
| 186 | break; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | }; | ||
| 190 | self.descriptors[index].set_ready(pkt.as_ptr() as u32, pkt.len()); | ||
| 191 | *buf = Some(pkt); | ||
| 192 | last_index = index; | ||
| 193 | } | ||
| 194 | self.next_tail_index = (last_index + 1) % N; | ||
| 195 | |||
| 196 | // not sure if this is supposed to span all of the descriptor or just those that contain buffers | ||
| 197 | { | ||
| 198 | let mut previous: Option<&mut RDes> = None; | ||
| 199 | for entry in self.descriptors.iter_mut() { | ||
| 200 | if let Some(prev) = &mut previous { | ||
| 201 | prev.setup(Some(entry)); | ||
| 202 | } | ||
| 203 | previous = Some(entry); | ||
| 204 | } | ||
| 205 | 143 | ||
| 206 | if let Some(entry) = &mut previous { | 144 | for (i, entry) in descriptors.iter().enumerate() { |
| 207 | entry.setup(None); | 145 | entry.setup(descriptors.get(i + 1), buffers[i].0.as_mut_ptr()); |
| 208 | } | ||
| 209 | } | 146 | } |
| 210 | 147 | ||
| 211 | // Register txdescriptor start | 148 | // Register rx descriptor start |
| 212 | // NOTE (unsafe) Used for atomic writes | 149 | // NOTE (unsafe) Used for atomic writes |
| 213 | unsafe { | 150 | unsafe { |
| 214 | ETH.ethernet_dma() | 151 | ETH.ethernet_dma() |
| 215 | .dmardlar() | 152 | .dmardlar() |
| 216 | .write(|w| w.0 = &self.descriptors as *const _ as u32); | 153 | .write(|w| w.0 = descriptors.as_ptr() as u32); |
| 217 | }; | 154 | }; |
| 218 | // We already have fences in `set_owned`, which is called in `setup` | 155 | // We already have fences in `set_owned`, which is called in `setup` |
| 219 | 156 | ||
| 220 | // Start receive | 157 | Self { |
| 221 | unsafe { ETH.ethernet_dma().dmaomr().modify(|w| w.set_sr(DmaomrSr::STARTED)) }; | 158 | descriptors, |
| 222 | 159 | buffers, | |
| 223 | self.demand_poll(); | 160 | index: 0, |
| 161 | } | ||
| 224 | } | 162 | } |
| 225 | 163 | ||
| 226 | fn demand_poll(&self) { | 164 | pub(crate) fn demand_poll(&self) { |
| 227 | unsafe { ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL)) }; | 165 | unsafe { ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL)) }; |
| 228 | } | 166 | } |
| 229 | 167 | ||
| 230 | pub(crate) fn on_interrupt(&mut self) { | ||
| 231 | // XXX: Do we need to do anything here ? Maybe we should try to advance the tail ptr, but it | ||
| 232 | // would soon hit the read ptr anyway, and we will wake smoltcp's stack on the interrupt | ||
| 233 | // which should try to pop a packet... | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Get current `RunningState` | 168 | /// Get current `RunningState` |
| 237 | fn running_state(&self) -> RunningState { | 169 | fn running_state(&self) -> RunningState { |
| 238 | match unsafe { ETH.ethernet_dma().dmasr().read().rps() } { | 170 | match unsafe { ETH.ethernet_dma().dmasr().read().rps() } { |
| @@ -252,52 +184,52 @@ impl<const N: usize> RDesRing<N> { | |||
| 252 | } | 184 | } |
| 253 | } | 185 | } |
| 254 | 186 | ||
| 255 | pub(crate) fn pop_packet(&mut self) -> Option<PacketBuf> { | 187 | /// Get a received packet if any, or None. |
| 256 | if !self.running_state().is_running() { | 188 | pub(crate) fn available(&mut self) -> Option<&mut [u8]> { |
| 189 | if self.running_state() != RunningState::Running { | ||
| 257 | self.demand_poll(); | 190 | self.demand_poll(); |
| 258 | } | 191 | } |
| 192 | |||
| 259 | // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using | 193 | // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using |
| 260 | // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the | 194 | // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the |
| 261 | // buffer (I think .-.) | 195 | // buffer (I think .-.) |
| 262 | fence(Ordering::SeqCst); | 196 | fence(Ordering::SeqCst); |
| 263 | 197 | ||
| 264 | let read_available = self.descriptors[self.read_index].available(); | 198 | // We might have to process many packets, in case some have been rx'd but are invalid. |
| 265 | let tail_index = (self.next_tail_index + N - 1) % N; | 199 | loop { |
| 200 | let descriptor = &mut self.descriptors[self.index]; | ||
| 201 | if !descriptor.available() { | ||
| 202 | return None; | ||
| 203 | } | ||
| 266 | 204 | ||
| 267 | let pkt = if read_available && self.read_index != tail_index { | 205 | // If packet is invalid, pop it and try again. |
| 268 | let pkt = self.buffers[self.read_index].take(); | 206 | if !descriptor.valid() { |
| 269 | let len = self.descriptors[self.read_index].packet_len(); | 207 | warn!("invalid packet: {:08x}", descriptor.rdes0.get()); |
| 208 | self.pop_packet(); | ||
| 209 | continue; | ||
| 210 | } | ||
| 270 | 211 | ||
| 271 | assert!(pkt.is_some()); | 212 | break; |
| 272 | let valid = self.descriptors[self.read_index].valid(); | 213 | } |
| 273 | 214 | ||
| 274 | self.read_index = (self.read_index + 1) % N; | 215 | let descriptor = &mut self.descriptors[self.index]; |
| 275 | if valid { | 216 | let len = descriptor.packet_len(); |
| 276 | pkt.map(|p| p.slice(0..len)) | 217 | return Some(&mut self.buffers[self.index].0[..len]); |
| 277 | } else { | 218 | } |
| 278 | None | ||
| 279 | } | ||
| 280 | } else { | ||
| 281 | None | ||
| 282 | }; | ||
| 283 | 219 | ||
| 284 | // Try to advance the tail_index | 220 | /// Pop the packet previously returned by `available`. |
| 285 | if self.next_tail_index != self.read_index { | 221 | pub(crate) fn pop_packet(&mut self) { |
| 286 | match PacketBox::new(Packet::new()) { | 222 | let descriptor = &mut self.descriptors[self.index]; |
| 287 | Some(b) => { | 223 | assert!(descriptor.available()); |
| 288 | let addr = b.as_ptr() as u32; | 224 | |
| 289 | let buffer_len = b.len(); | 225 | self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr()); |
| 290 | self.buffers[self.next_tail_index].replace(b); | 226 | |
| 291 | self.descriptors[self.next_tail_index].set_ready(addr, buffer_len); | 227 | self.demand_poll(); |
| 292 | 228 | ||
| 293 | // "Preceding reads and writes cannot be moved past subsequent writes." | 229 | // Increment index. |
| 294 | fence(Ordering::Release); | 230 | self.index += 1; |
| 295 | 231 | if self.index == self.descriptors.len() { | |
| 296 | self.next_tail_index = (self.next_tail_index + 1) % N; | 232 | self.index = 0 |
| 297 | } | ||
| 298 | None => {} | ||
| 299 | } | ||
| 300 | } | 233 | } |
| 301 | pkt | ||
| 302 | } | 234 | } |
| 303 | } | 235 | } |
diff --git a/embassy-stm32/src/eth/v1/tx_desc.rs b/embassy-stm32/src/eth/v1/tx_desc.rs index f2889b550..0e63c5443 100644 --- a/embassy-stm32/src/eth/v1/tx_desc.rs +++ b/embassy-stm32/src/eth/v1/tx_desc.rs | |||
| @@ -1,20 +1,10 @@ | |||
| 1 | use core::sync::atomic::{compiler_fence, fence, Ordering}; | 1 | use core::sync::atomic::{compiler_fence, fence, Ordering}; |
| 2 | 2 | ||
| 3 | use embassy_net::PacketBuf; | ||
| 4 | use stm32_metapac::eth::vals::St; | ||
| 5 | use vcell::VolatileCell; | 3 | use vcell::VolatileCell; |
| 6 | 4 | ||
| 5 | use crate::eth::TX_BUFFER_SIZE; | ||
| 7 | use crate::pac::ETH; | 6 | use crate::pac::ETH; |
| 8 | 7 | ||
| 9 | #[non_exhaustive] | ||
| 10 | #[derive(Debug, Copy, Clone)] | ||
| 11 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 12 | pub enum Error { | ||
| 13 | NoBufferAvailable, | ||
| 14 | // TODO: Break down this error into several others | ||
| 15 | TransmissionError, | ||
| 16 | } | ||
| 17 | |||
| 18 | /// Transmit and Receive Descriptor fields | 8 | /// Transmit and Receive Descriptor fields |
| 19 | #[allow(dead_code)] | 9 | #[allow(dead_code)] |
| 20 | mod tx_consts { | 10 | mod tx_consts { |
| @@ -37,6 +27,8 @@ mod tx_consts { | |||
| 37 | } | 27 | } |
| 38 | use tx_consts::*; | 28 | use tx_consts::*; |
| 39 | 29 | ||
| 30 | use super::Packet; | ||
| 31 | |||
| 40 | /// Transmit Descriptor representation | 32 | /// Transmit Descriptor representation |
| 41 | /// | 33 | /// |
| 42 | /// * tdes0: control | 34 | /// * tdes0: control |
| @@ -44,7 +36,7 @@ use tx_consts::*; | |||
| 44 | /// * tdes2: data buffer address | 36 | /// * tdes2: data buffer address |
| 45 | /// * tdes3: next descriptor address | 37 | /// * tdes3: next descriptor address |
| 46 | #[repr(C)] | 38 | #[repr(C)] |
| 47 | struct TDes { | 39 | pub(crate) struct TDes { |
| 48 | tdes0: VolatileCell<u32>, | 40 | tdes0: VolatileCell<u32>, |
| 49 | tdes1: VolatileCell<u32>, | 41 | tdes1: VolatileCell<u32>, |
| 50 | tdes2: VolatileCell<u32>, | 42 | tdes2: VolatileCell<u32>, |
| @@ -62,7 +54,7 @@ impl TDes { | |||
| 62 | } | 54 | } |
| 63 | 55 | ||
| 64 | /// Return true if this TDes is not currently owned by the DMA | 56 | /// Return true if this TDes is not currently owned by the DMA |
| 65 | pub fn available(&self) -> bool { | 57 | fn available(&self) -> bool { |
| 66 | (self.tdes0.get() & TXDESC_0_OWN) == 0 | 58 | (self.tdes0.get() & TXDESC_0_OWN) == 0 |
| 67 | } | 59 | } |
| 68 | 60 | ||
| @@ -79,26 +71,26 @@ impl TDes { | |||
| 79 | fence(Ordering::SeqCst); | 71 | fence(Ordering::SeqCst); |
| 80 | } | 72 | } |
| 81 | 73 | ||
| 82 | fn set_buffer1(&mut self, buffer: *const u8) { | 74 | fn set_buffer1(&self, buffer: *const u8) { |
| 83 | self.tdes2.set(buffer as u32); | 75 | self.tdes2.set(buffer as u32); |
| 84 | } | 76 | } |
| 85 | 77 | ||
| 86 | fn set_buffer1_len(&mut self, len: usize) { | 78 | fn set_buffer1_len(&self, len: usize) { |
| 87 | self.tdes1 | 79 | self.tdes1 |
| 88 | .set((self.tdes1.get() & !TXDESC_1_TBS_MASK) | ((len as u32) << TXDESC_1_TBS_SHIFT)); | 80 | .set((self.tdes1.get() & !TXDESC_1_TBS_MASK) | ((len as u32) << TXDESC_1_TBS_SHIFT)); |
| 89 | } | 81 | } |
| 90 | 82 | ||
| 91 | // points to next descriptor (RCH) | 83 | // points to next descriptor (RCH) |
| 92 | fn set_buffer2(&mut self, buffer: *const u8) { | 84 | fn set_buffer2(&self, buffer: *const u8) { |
| 93 | self.tdes3.set(buffer as u32); | 85 | self.tdes3.set(buffer as u32); |
| 94 | } | 86 | } |
| 95 | 87 | ||
| 96 | fn set_end_of_ring(&mut self) { | 88 | fn set_end_of_ring(&self) { |
| 97 | self.tdes0.set(self.tdes0.get() | TXDESC_0_TER); | 89 | self.tdes0.set(self.tdes0.get() | TXDESC_0_TER); |
| 98 | } | 90 | } |
| 99 | 91 | ||
| 100 | // set up as a part fo the ring buffer - configures the tdes | 92 | // set up as a part fo the ring buffer - configures the tdes |
| 101 | pub fn setup(&mut self, next: Option<&Self>) { | 93 | fn setup(&self, next: Option<&Self>) { |
| 102 | // Defer this initialization to this function, so we can have `RingEntry` on bss. | 94 | // Defer this initialization to this function, so we can have `RingEntry` on bss. |
| 103 | self.tdes0.set(TXDESC_0_TCH | TXDESC_0_IOC | TXDESC_0_FS | TXDESC_0_LS); | 95 | self.tdes0.set(TXDESC_0_TCH | TXDESC_0_IOC | TXDESC_0_FS | TXDESC_0_LS); |
| 104 | match next { | 96 | match next { |
| @@ -111,85 +103,58 @@ impl TDes { | |||
| 111 | } | 103 | } |
| 112 | } | 104 | } |
| 113 | 105 | ||
| 114 | pub(crate) struct TDesRing<const N: usize> { | 106 | pub(crate) struct TDesRing<'a> { |
| 115 | descriptors: [TDes; N], | 107 | descriptors: &'a mut [TDes], |
| 116 | buffers: [Option<PacketBuf>; N], | 108 | buffers: &'a mut [Packet<TX_BUFFER_SIZE>], |
| 117 | next_entry: usize, | 109 | index: usize, |
| 118 | } | 110 | } |
| 119 | 111 | ||
| 120 | impl<const N: usize> TDesRing<N> { | 112 | impl<'a> TDesRing<'a> { |
| 121 | pub const fn new() -> Self { | ||
| 122 | const TDES: TDes = TDes::new(); | ||
| 123 | const BUFFERS: Option<PacketBuf> = None; | ||
| 124 | |||
| 125 | Self { | ||
| 126 | descriptors: [TDES; N], | ||
| 127 | buffers: [BUFFERS; N], | ||
| 128 | next_entry: 0, | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Initialise this TDesRing. Assume TDesRing is corrupt | 113 | /// Initialise this TDesRing. Assume TDesRing is corrupt |
| 133 | /// | 114 | pub(crate) fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet<TX_BUFFER_SIZE>]) -> Self { |
| 134 | /// The current memory address of the buffers inside this TDesRing | 115 | assert!(descriptors.len() > 0); |
| 135 | /// will be stored in the descriptors, so ensure the TDesRing is | 116 | assert!(descriptors.len() == buffers.len()); |
| 136 | /// not moved after initialisation. | ||
| 137 | pub(crate) fn init(&mut self) { | ||
| 138 | assert!(N > 0); | ||
| 139 | |||
| 140 | { | ||
| 141 | let mut previous: Option<&mut TDes> = None; | ||
| 142 | for entry in self.descriptors.iter_mut() { | ||
| 143 | if let Some(prev) = &mut previous { | ||
| 144 | prev.setup(Some(entry)); | ||
| 145 | } | ||
| 146 | previous = Some(entry); | ||
| 147 | } | ||
| 148 | 117 | ||
| 149 | if let Some(entry) = &mut previous { | 118 | for (i, entry) in descriptors.iter().enumerate() { |
| 150 | entry.setup(None); | 119 | entry.setup(descriptors.get(i + 1)); |
| 151 | } | ||
| 152 | } | 120 | } |
| 153 | self.next_entry = 0; | ||
| 154 | 121 | ||
| 155 | // Register txdescriptor start | 122 | // Register txdescriptor start |
| 156 | // NOTE (unsafe) Used for atomic writes | 123 | // NOTE (unsafe) Used for atomic writes |
| 157 | unsafe { | 124 | unsafe { |
| 158 | ETH.ethernet_dma() | 125 | ETH.ethernet_dma() |
| 159 | .dmatdlar() | 126 | .dmatdlar() |
| 160 | .write(|w| w.0 = &self.descriptors as *const _ as u32); | 127 | .write(|w| w.0 = descriptors.as_ptr() as u32); |
| 161 | } | 128 | } |
| 162 | 129 | ||
| 163 | // "Preceding reads and writes cannot be moved past subsequent writes." | 130 | Self { |
| 164 | #[cfg(feature = "fence")] | 131 | descriptors, |
| 165 | fence(Ordering::Release); | 132 | buffers, |
| 166 | 133 | index: 0, | |
| 167 | // We don't need a compiler fence here because all interactions with `Descriptor` are | 134 | } |
| 168 | // volatiles | ||
| 169 | |||
| 170 | // Start transmission | ||
| 171 | unsafe { ETH.ethernet_dma().dmaomr().modify(|w| w.set_st(St::STARTED)) }; | ||
| 172 | } | 135 | } |
| 173 | 136 | ||
| 174 | /// Return true if a TDes is available for use | 137 | pub(crate) fn len(&self) -> usize { |
| 175 | pub(crate) fn available(&self) -> bool { | 138 | self.descriptors.len() |
| 176 | self.descriptors[self.next_entry].available() | ||
| 177 | } | 139 | } |
| 178 | 140 | ||
| 179 | pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> { | 141 | /// Return the next available packet buffer for transmitting, or None |
| 180 | if !self.available() { | 142 | pub(crate) fn available(&mut self) -> Option<&mut [u8]> { |
| 181 | return Err(Error::NoBufferAvailable); | 143 | let descriptor = &mut self.descriptors[self.index]; |
| 144 | if descriptor.available() { | ||
| 145 | Some(&mut self.buffers[self.index].0) | ||
| 146 | } else { | ||
| 147 | None | ||
| 182 | } | 148 | } |
| 149 | } | ||
| 183 | 150 | ||
| 184 | let descriptor = &mut self.descriptors[self.next_entry]; | 151 | /// Transmit the packet written in a buffer returned by `available`. |
| 185 | 152 | pub(crate) fn transmit(&mut self, len: usize) { | |
| 186 | let pkt_len = pkt.len(); | 153 | let descriptor = &mut self.descriptors[self.index]; |
| 187 | let address = pkt.as_ptr() as *const u8; | 154 | assert!(descriptor.available()); |
| 188 | |||
| 189 | descriptor.set_buffer1(address); | ||
| 190 | descriptor.set_buffer1_len(pkt_len); | ||
| 191 | 155 | ||
| 192 | self.buffers[self.next_entry].replace(pkt); | 156 | descriptor.set_buffer1(self.buffers[self.index].0.as_ptr()); |
| 157 | descriptor.set_buffer1_len(len); | ||
| 193 | 158 | ||
| 194 | descriptor.set_owned(); | 159 | descriptor.set_owned(); |
| 195 | 160 | ||
| @@ -198,36 +163,12 @@ impl<const N: usize> TDesRing<N> { | |||
| 198 | // "Preceding reads and writes cannot be moved past subsequent writes." | 163 | // "Preceding reads and writes cannot be moved past subsequent writes." |
| 199 | fence(Ordering::Release); | 164 | fence(Ordering::Release); |
| 200 | 165 | ||
| 201 | // Move the tail pointer (TPR) to the next descriptor | 166 | // Move the index to the next descriptor |
| 202 | self.next_entry = (self.next_entry + 1) % N; | 167 | self.index += 1; |
| 203 | 168 | if self.index == self.descriptors.len() { | |
| 169 | self.index = 0 | ||
| 170 | } | ||
| 204 | // Request the DMA engine to poll the latest tx descriptor | 171 | // Request the DMA engine to poll the latest tx descriptor |
| 205 | unsafe { ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1) } | 172 | unsafe { ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1) } |
| 206 | Ok(()) | ||
| 207 | } | ||
| 208 | |||
| 209 | pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> { | ||
| 210 | let previous = (self.next_entry + N - 1) % N; | ||
| 211 | let td = &self.descriptors[previous]; | ||
| 212 | |||
| 213 | // DMB to ensure that we are reading an updated value, probably not needed at the hardware | ||
| 214 | // level, but this is also a hint to the compiler that we're syncing on the buffer. | ||
| 215 | fence(Ordering::SeqCst); | ||
| 216 | |||
| 217 | let tdes0 = td.tdes0.get(); | ||
| 218 | |||
| 219 | if tdes0 & TXDESC_0_OWN != 0 { | ||
| 220 | // Transmission isn't done yet, probably a receive interrupt that fired this | ||
| 221 | return Ok(()); | ||
| 222 | } | ||
| 223 | |||
| 224 | // Release the buffer | ||
| 225 | self.buffers[previous].take(); | ||
| 226 | |||
| 227 | if tdes0 & TXDESC_0_ES != 0 { | ||
| 228 | Err(Error::TransmissionError) | ||
| 229 | } else { | ||
| 230 | Ok(()) | ||
| 231 | } | ||
| 232 | } | 173 | } |
| 233 | } | 174 | } |
diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs index c6c06a9ce..2426596fb 100644 --- a/embassy-stm32/src/eth/v2/descriptors.rs +++ b/embassy-stm32/src/eth/v2/descriptors.rs | |||
| @@ -1,19 +1,10 @@ | |||
| 1 | use core::sync::atomic::{fence, Ordering}; | 1 | use core::sync::atomic::{fence, Ordering}; |
| 2 | 2 | ||
| 3 | use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf}; | ||
| 4 | use vcell::VolatileCell; | 3 | use vcell::VolatileCell; |
| 5 | 4 | ||
| 5 | use crate::eth::{Packet, RX_BUFFER_SIZE, TX_BUFFER_SIZE}; | ||
| 6 | use crate::pac::ETH; | 6 | use crate::pac::ETH; |
| 7 | 7 | ||
| 8 | #[non_exhaustive] | ||
| 9 | #[derive(Debug, Copy, Clone)] | ||
| 10 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 11 | pub enum Error { | ||
| 12 | NoBufferAvailable, | ||
| 13 | // TODO: Break down this error into several others | ||
| 14 | TransmissionError, | ||
| 15 | } | ||
| 16 | |||
| 17 | /// Transmit and Receive Descriptor fields | 8 | /// Transmit and Receive Descriptor fields |
| 18 | #[allow(dead_code)] | 9 | #[allow(dead_code)] |
| 19 | mod emac_consts { | 10 | mod emac_consts { |
| @@ -41,7 +32,7 @@ use emac_consts::*; | |||
| 41 | /// * tdes2: buffer lengths | 32 | /// * tdes2: buffer lengths |
| 42 | /// * tdes3: control and payload/frame length | 33 | /// * tdes3: control and payload/frame length |
| 43 | #[repr(C)] | 34 | #[repr(C)] |
| 44 | struct TDes { | 35 | pub(crate) struct TDes { |
| 45 | tdes0: VolatileCell<u32>, | 36 | tdes0: VolatileCell<u32>, |
| 46 | tdes1: VolatileCell<u32>, | 37 | tdes1: VolatileCell<u32>, |
| 47 | tdes2: VolatileCell<u32>, | 38 | tdes2: VolatileCell<u32>, |
| @@ -59,41 +50,26 @@ impl TDes { | |||
| 59 | } | 50 | } |
| 60 | 51 | ||
| 61 | /// Return true if this TDes is not currently owned by the DMA | 52 | /// Return true if this TDes is not currently owned by the DMA |
| 62 | pub fn available(&self) -> bool { | 53 | fn available(&self) -> bool { |
| 63 | self.tdes3.get() & EMAC_DES3_OWN == 0 | 54 | self.tdes3.get() & EMAC_DES3_OWN == 0 |
| 64 | } | 55 | } |
| 65 | } | 56 | } |
| 66 | 57 | ||
| 67 | pub(crate) struct TDesRing<const N: usize> { | 58 | pub(crate) struct TDesRing<'a> { |
| 68 | td: [TDes; N], | 59 | descriptors: &'a mut [TDes], |
| 69 | buffers: [Option<PacketBuf>; N], | 60 | buffers: &'a mut [Packet<TX_BUFFER_SIZE>], |
| 70 | tdidx: usize, | 61 | index: usize, |
| 71 | } | 62 | } |
| 72 | 63 | ||
| 73 | impl<const N: usize> TDesRing<N> { | 64 | impl<'a> TDesRing<'a> { |
| 74 | pub const fn new() -> Self { | 65 | /// Initialise this TDesRing. Assume TDesRing is corrupt. |
| 75 | const TDES: TDes = TDes::new(); | 66 | pub fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet<TX_BUFFER_SIZE>]) -> Self { |
| 76 | const BUFFERS: Option<PacketBuf> = None; | 67 | assert!(descriptors.len() > 0); |
| 68 | assert!(descriptors.len() == buffers.len()); | ||
| 77 | 69 | ||
| 78 | Self { | 70 | for td in descriptors.iter_mut() { |
| 79 | td: [TDES; N], | ||
| 80 | buffers: [BUFFERS; N], | ||
| 81 | tdidx: 0, | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Initialise this TDesRing. Assume TDesRing is corrupt | ||
| 86 | /// | ||
| 87 | /// The current memory address of the buffers inside this TDesRing | ||
| 88 | /// will be stored in the descriptors, so ensure the TDesRing is | ||
| 89 | /// not moved after initialisation. | ||
| 90 | pub(crate) fn init(&mut self) { | ||
| 91 | assert!(N > 0); | ||
| 92 | |||
| 93 | for td in self.td.iter_mut() { | ||
| 94 | *td = TDes::new(); | 71 | *td = TDes::new(); |
| 95 | } | 72 | } |
| 96 | self.tdidx = 0; | ||
| 97 | 73 | ||
| 98 | // Initialize the pointers in the DMA engine. (There will be a memory barrier later | 74 | // Initialize the pointers in the DMA engine. (There will be a memory barrier later |
| 99 | // before the DMA engine is enabled.) | 75 | // before the DMA engine is enabled.) |
| @@ -101,80 +77,60 @@ impl<const N: usize> TDesRing<N> { | |||
| 101 | unsafe { | 77 | unsafe { |
| 102 | let dma = ETH.ethernet_dma(); | 78 | let dma = ETH.ethernet_dma(); |
| 103 | 79 | ||
| 104 | dma.dmactx_dlar().write(|w| w.0 = &self.td as *const _ as u32); | 80 | dma.dmactx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32); |
| 105 | dma.dmactx_rlr().write(|w| w.set_tdrl((N as u16) - 1)); | 81 | dma.dmactx_rlr().write(|w| w.set_tdrl((descriptors.len() as u16) - 1)); |
| 106 | dma.dmactx_dtpr().write(|w| w.0 = &self.td[0] as *const _ as u32); | 82 | dma.dmactx_dtpr().write(|w| w.0 = 0); |
| 83 | } | ||
| 84 | |||
| 85 | Self { | ||
| 86 | descriptors, | ||
| 87 | buffers, | ||
| 88 | index: 0, | ||
| 107 | } | 89 | } |
| 108 | } | 90 | } |
| 109 | 91 | ||
| 110 | /// Return true if a TDes is available for use | 92 | pub(crate) fn len(&self) -> usize { |
| 111 | pub(crate) fn available(&self) -> bool { | 93 | self.descriptors.len() |
| 112 | self.td[self.tdidx].available() | ||
| 113 | } | 94 | } |
| 114 | 95 | ||
| 115 | pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> { | 96 | /// Return the next available packet buffer for transmitting, or None |
| 116 | if !self.available() { | 97 | pub(crate) fn available(&mut self) -> Option<&mut [u8]> { |
| 117 | return Err(Error::NoBufferAvailable); | 98 | let d = &mut self.descriptors[self.index]; |
| 99 | if d.available() { | ||
| 100 | Some(&mut self.buffers[self.index].0) | ||
| 101 | } else { | ||
| 102 | None | ||
| 118 | } | 103 | } |
| 119 | let x = self.tdidx; | 104 | } |
| 120 | let td = &mut self.td[x]; | ||
| 121 | 105 | ||
| 122 | let pkt_len = pkt.len(); | 106 | /// Transmit the packet written in a buffer returned by `available`. |
| 123 | assert!(pkt_len as u32 <= EMAC_TDES2_B1L); | 107 | pub(crate) fn transmit(&mut self, len: usize) { |
| 124 | let address = pkt.as_ptr() as u32; | 108 | let td = &mut self.descriptors[self.index]; |
| 109 | assert!(td.available()); | ||
| 110 | assert!(len as u32 <= EMAC_TDES2_B1L); | ||
| 125 | 111 | ||
| 126 | // Read format | 112 | // Read format |
| 127 | td.tdes0.set(address); | 113 | td.tdes0.set(self.buffers[self.index].0.as_ptr() as u32); |
| 128 | td.tdes2.set(pkt_len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC); | 114 | td.tdes2.set(len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC); |
| 129 | 115 | ||
| 130 | // FD: Contains first buffer of packet | 116 | // FD: Contains first buffer of packet |
| 131 | // LD: Contains last buffer of packet | 117 | // LD: Contains last buffer of packet |
| 132 | // Give the DMA engine ownership | 118 | // Give the DMA engine ownership |
| 133 | td.tdes3.set(EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_OWN); | 119 | td.tdes3.set(EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_OWN); |
| 134 | 120 | ||
| 135 | self.buffers[x].replace(pkt); | ||
| 136 | |||
| 137 | // Ensure changes to the descriptor are committed before DMA engine sees tail pointer store. | 121 | // Ensure changes to the descriptor are committed before DMA engine sees tail pointer store. |
| 138 | // This will generate an DMB instruction. | 122 | // This will generate an DMB instruction. |
| 139 | // "Preceding reads and writes cannot be moved past subsequent writes." | 123 | // "Preceding reads and writes cannot be moved past subsequent writes." |
| 140 | fence(Ordering::Release); | 124 | fence(Ordering::Release); |
| 141 | 125 | ||
| 142 | // Move the tail pointer (TPR) to the next descriptor | 126 | self.index = self.index + 1; |
| 143 | let x = (x + 1) % N; | 127 | if self.index == self.descriptors.len() { |
| 144 | // NOTE(unsafe) Atomic write | 128 | self.index = 0; |
| 145 | unsafe { | ||
| 146 | ETH.ethernet_dma() | ||
| 147 | .dmactx_dtpr() | ||
| 148 | .write(|w| w.0 = &self.td[x] as *const _ as u32); | ||
| 149 | } | 129 | } |
| 150 | self.tdidx = x; | ||
| 151 | Ok(()) | ||
| 152 | } | ||
| 153 | |||
| 154 | pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> { | ||
| 155 | let previous = (self.tdidx + N - 1) % N; | ||
| 156 | let td = &self.td[previous]; | ||
| 157 | |||
| 158 | // DMB to ensure that we are reading an updated value, probably not needed at the hardware | ||
| 159 | // level, but this is also a hint to the compiler that we're syncing on the buffer. | ||
| 160 | fence(Ordering::SeqCst); | ||
| 161 | |||
| 162 | let tdes3 = td.tdes3.get(); | ||
| 163 | |||
| 164 | if tdes3 & EMAC_DES3_OWN != 0 { | ||
| 165 | // Transmission isn't done yet, probably a receive interrupt that fired this | ||
| 166 | return Ok(()); | ||
| 167 | } | ||
| 168 | assert!(tdes3 & EMAC_DES3_CTXT == 0); | ||
| 169 | |||
| 170 | // Release the buffer | ||
| 171 | self.buffers[previous].take(); | ||
| 172 | 130 | ||
| 173 | if tdes3 & EMAC_DES3_ES != 0 { | 131 | // signal DMA it can try again. |
| 174 | Err(Error::TransmissionError) | 132 | // NOTE(unsafe) Atomic write |
| 175 | } else { | 133 | unsafe { ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0) } |
| 176 | Ok(()) | ||
| 177 | } | ||
| 178 | } | 134 | } |
| 179 | } | 135 | } |
| 180 | 136 | ||
| @@ -185,7 +141,7 @@ impl<const N: usize> TDesRing<N> { | |||
| 185 | /// * rdes2: | 141 | /// * rdes2: |
| 186 | /// * rdes3: OWN and Status | 142 | /// * rdes3: OWN and Status |
| 187 | #[repr(C)] | 143 | #[repr(C)] |
| 188 | struct RDes { | 144 | pub(crate) struct RDes { |
| 189 | rdes0: VolatileCell<u32>, | 145 | rdes0: VolatileCell<u32>, |
| 190 | rdes1: VolatileCell<u32>, | 146 | rdes1: VolatileCell<u32>, |
| 191 | rdes2: VolatileCell<u32>, | 147 | rdes2: VolatileCell<u32>, |
| @@ -204,7 +160,7 @@ impl RDes { | |||
| 204 | 160 | ||
| 205 | /// Return true if this RDes is acceptable to us | 161 | /// Return true if this RDes is acceptable to us |
| 206 | #[inline(always)] | 162 | #[inline(always)] |
| 207 | pub fn valid(&self) -> bool { | 163 | fn valid(&self) -> bool { |
| 208 | // Write-back descriptor is valid if: | 164 | // Write-back descriptor is valid if: |
| 209 | // | 165 | // |
| 210 | // Contains first buffer of packet AND contains last buf of | 166 | // Contains first buffer of packet AND contains last buf of |
| @@ -215,177 +171,96 @@ impl RDes { | |||
| 215 | 171 | ||
| 216 | /// Return true if this RDes is not currently owned by the DMA | 172 | /// Return true if this RDes is not currently owned by the DMA |
| 217 | #[inline(always)] | 173 | #[inline(always)] |
| 218 | pub fn available(&self) -> bool { | 174 | fn available(&self) -> bool { |
| 219 | self.rdes3.get() & EMAC_DES3_OWN == 0 // Owned by us | 175 | self.rdes3.get() & EMAC_DES3_OWN == 0 // Owned by us |
| 220 | } | 176 | } |
| 221 | 177 | ||
| 222 | #[inline(always)] | 178 | #[inline(always)] |
| 223 | pub fn set_ready(&mut self, buf_addr: u32) { | 179 | fn set_ready(&mut self, buf: *mut u8) { |
| 224 | self.rdes0.set(buf_addr); | 180 | self.rdes0.set(buf as u32); |
| 225 | self.rdes3.set(EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN); | 181 | self.rdes3.set(EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN); |
| 226 | } | 182 | } |
| 227 | } | 183 | } |
| 228 | 184 | ||
| 229 | /// Rx ring of descriptors and packets | 185 | /// Rx ring of descriptors and packets |
| 230 | /// | 186 | pub(crate) struct RDesRing<'a> { |
| 231 | /// This ring has three major locations that work in lock-step. The DMA will never write to the tail | 187 | descriptors: &'a mut [RDes], |
| 232 | /// index, so the `read_index` must never pass the tail index. The `next_tail_index` is always 1 | 188 | buffers: &'a mut [Packet<RX_BUFFER_SIZE>], |
| 233 | /// slot ahead of the real tail index, and it must never pass the `read_index` or it could overwrite | 189 | index: usize, |
| 234 | /// a packet still to be passed to the application. | ||
| 235 | /// | ||
| 236 | /// nt can't pass r (no alloc) | ||
| 237 | /// +---+---+---+---+ Read ok +---+---+---+---+ No Read +---+---+---+---+ | ||
| 238 | /// | | | | | ------------> | | | | | ------------> | | | | | | ||
| 239 | /// +---+---+---+---+ Allocation ok +---+---+---+---+ +---+---+---+---+ | ||
| 240 | /// ^ ^t ^t ^ ^t ^ | ||
| 241 | /// |r |r |r | ||
| 242 | /// |nt |nt |nt | ||
| 243 | /// | ||
| 244 | /// | ||
| 245 | /// +---+---+---+---+ Read ok +---+---+---+---+ Can't read +---+---+---+---+ | ||
| 246 | /// | | | | | ------------> | | | | | ------------> | | | | | | ||
| 247 | /// +---+---+---+---+ Allocation fail +---+---+---+---+ Allocation ok +---+---+---+---+ | ||
| 248 | /// ^ ^t ^ ^t ^ ^ ^ ^t | ||
| 249 | /// |r | |r | | |r | ||
| 250 | /// |nt |nt |nt | ||
| 251 | /// | ||
| 252 | pub(crate) struct RDesRing<const N: usize> { | ||
| 253 | rd: [RDes; N], | ||
| 254 | buffers: [Option<PacketBox>; N], | ||
| 255 | read_idx: usize, | ||
| 256 | next_tail_idx: usize, | ||
| 257 | } | 190 | } |
| 258 | 191 | ||
| 259 | impl<const N: usize> RDesRing<N> { | 192 | impl<'a> RDesRing<'a> { |
| 260 | pub const fn new() -> Self { | 193 | pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet<RX_BUFFER_SIZE>]) -> Self { |
| 261 | const RDES: RDes = RDes::new(); | 194 | assert!(descriptors.len() > 1); |
| 262 | const BUFFERS: Option<PacketBox> = None; | 195 | assert!(descriptors.len() == buffers.len()); |
| 263 | |||
| 264 | Self { | ||
| 265 | rd: [RDES; N], | ||
| 266 | buffers: [BUFFERS; N], | ||
| 267 | read_idx: 0, | ||
| 268 | next_tail_idx: 0, | ||
| 269 | } | ||
| 270 | } | ||
| 271 | 196 | ||
| 272 | pub(crate) fn init(&mut self) { | 197 | for (i, desc) in descriptors.iter_mut().enumerate() { |
| 273 | assert!(N > 1); | ||
| 274 | |||
| 275 | for desc in self.rd.iter_mut() { | ||
| 276 | *desc = RDes::new(); | 198 | *desc = RDes::new(); |
| 199 | desc.set_ready(buffers[i].0.as_mut_ptr()); | ||
| 277 | } | 200 | } |
| 278 | 201 | ||
| 279 | let mut last_index = 0; | ||
| 280 | for (index, buf) in self.buffers.iter_mut().enumerate() { | ||
| 281 | let pkt = match PacketBox::new(Packet::new()) { | ||
| 282 | Some(p) => p, | ||
| 283 | None => { | ||
| 284 | if index == 0 { | ||
| 285 | panic!("Could not allocate at least one buffer for Ethernet receiving"); | ||
| 286 | } else { | ||
| 287 | break; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | }; | ||
| 291 | let addr = pkt.as_ptr() as u32; | ||
| 292 | *buf = Some(pkt); | ||
| 293 | self.rd[index].set_ready(addr); | ||
| 294 | last_index = index; | ||
| 295 | } | ||
| 296 | self.next_tail_idx = (last_index + 1) % N; | ||
| 297 | |||
| 298 | unsafe { | 202 | unsafe { |
| 299 | let dma = ETH.ethernet_dma(); | 203 | let dma = ETH.ethernet_dma(); |
| 300 | 204 | ||
| 301 | dma.dmacrx_dlar().write(|w| w.0 = self.rd.as_ptr() as u32); | 205 | dma.dmacrx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32); |
| 302 | dma.dmacrx_rlr().write(|w| w.set_rdrl((N as u16) - 1)); | 206 | dma.dmacrx_rlr().write(|w| w.set_rdrl((descriptors.len() as u16) - 1)); |
| 303 | 207 | dma.dmacrx_dtpr().write(|w| w.0 = 0); | |
| 304 | // We manage to allocate all buffers, set the index to the last one, that means | ||
| 305 | // that the DMA won't consider the last one as ready, because it (unfortunately) | ||
| 306 | // stops at the tail ptr and wraps at the end of the ring, which means that we | ||
| 307 | // can't tell it to stop after the last buffer. | ||
| 308 | let tail_ptr = &self.rd[last_index] as *const _ as u32; | ||
| 309 | fence(Ordering::Release); | ||
| 310 | |||
| 311 | dma.dmacrx_dtpr().write(|w| w.0 = tail_ptr); | ||
| 312 | } | 208 | } |
| 313 | } | ||
| 314 | 209 | ||
| 315 | pub(crate) fn on_interrupt(&mut self) { | 210 | Self { |
| 316 | // XXX: Do we need to do anything here ? Maybe we should try to advance the tail ptr, but it | 211 | descriptors, |
| 317 | // would soon hit the read ptr anyway, and we will wake smoltcp's stack on the interrupt | 212 | buffers, |
| 318 | // which should try to pop a packet... | 213 | index: 0, |
| 214 | } | ||
| 319 | } | 215 | } |
| 320 | 216 | ||
| 321 | pub(crate) fn pop_packet(&mut self) -> Option<PacketBuf> { | 217 | /// Get a received packet if any, or None. |
| 218 | pub(crate) fn available(&mut self) -> Option<&mut [u8]> { | ||
| 322 | // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using | 219 | // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using |
| 323 | // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the | 220 | // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the |
| 324 | // buffer (I think .-.) | 221 | // buffer (I think .-.) |
| 325 | fence(Ordering::SeqCst); | 222 | fence(Ordering::SeqCst); |
| 326 | 223 | ||
| 327 | let read_available = self.rd[self.read_idx].available(); | 224 | // We might have to process many packets, in case some have been rx'd but are invalid. |
| 328 | let tail_index = (self.next_tail_idx + N - 1) % N; | 225 | loop { |
| 329 | 226 | let descriptor = &mut self.descriptors[self.index]; | |
| 330 | let pkt = if read_available && self.read_idx != tail_index { | 227 | if !descriptor.available() { |
| 331 | let pkt = self.buffers[self.read_idx].take(); | 228 | return None; |
| 332 | let len = (self.rd[self.read_idx].rdes3.get() & EMAC_RDES3_PKTLEN) as usize; | ||
| 333 | |||
| 334 | assert!(pkt.is_some()); | ||
| 335 | let valid = self.rd[self.read_idx].valid(); | ||
| 336 | |||
| 337 | self.read_idx = (self.read_idx + 1) % N; | ||
| 338 | if valid { | ||
| 339 | pkt.map(|p| p.slice(0..len)) | ||
| 340 | } else { | ||
| 341 | None | ||
| 342 | } | 229 | } |
| 343 | } else { | 230 | |
| 344 | None | 231 | // If packet is invalid, pop it and try again. |
| 345 | }; | 232 | if !descriptor.valid() { |
| 346 | 233 | warn!("invalid packet: {:08x}", descriptor.rdes0.get()); | |
| 347 | // Try to advance the tail_idx | 234 | self.pop_packet(); |
| 348 | if self.next_tail_idx != self.read_idx { | 235 | continue; |
| 349 | match PacketBox::new(Packet::new()) { | ||
| 350 | Some(b) => { | ||
| 351 | let addr = b.as_ptr() as u32; | ||
| 352 | self.buffers[self.next_tail_idx].replace(b); | ||
| 353 | self.rd[self.next_tail_idx].set_ready(addr); | ||
| 354 | |||
| 355 | // "Preceding reads and writes cannot be moved past subsequent writes." | ||
| 356 | fence(Ordering::Release); | ||
| 357 | |||
| 358 | // NOTE(unsafe) atomic write | ||
| 359 | unsafe { | ||
| 360 | ETH.ethernet_dma() | ||
| 361 | .dmacrx_dtpr() | ||
| 362 | .write(|w| w.0 = &self.rd[self.next_tail_idx] as *const _ as u32); | ||
| 363 | } | ||
| 364 | |||
| 365 | self.next_tail_idx = (self.next_tail_idx + 1) % N; | ||
| 366 | } | ||
| 367 | None => {} | ||
| 368 | } | 236 | } |
| 237 | |||
| 238 | break; | ||
| 369 | } | 239 | } |
| 370 | pkt | 240 | |
| 241 | let descriptor = &mut self.descriptors[self.index]; | ||
| 242 | let len = (descriptor.rdes3.get() & EMAC_RDES3_PKTLEN) as usize; | ||
| 243 | return Some(&mut self.buffers[self.index].0[..len]); | ||
| 371 | } | 244 | } |
| 372 | } | ||
| 373 | 245 | ||
| 374 | pub struct DescriptorRing<const T: usize, const R: usize> { | 246 | /// Pop the packet previously returned by `available`. |
| 375 | pub(crate) tx: TDesRing<T>, | 247 | pub(crate) fn pop_packet(&mut self) { |
| 376 | pub(crate) rx: RDesRing<R>, | 248 | let descriptor = &mut self.descriptors[self.index]; |
| 377 | } | 249 | assert!(descriptor.available()); |
| 378 | 250 | ||
| 379 | impl<const T: usize, const R: usize> DescriptorRing<T, R> { | 251 | self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr()); |
| 380 | pub const fn new() -> Self { | 252 | |
| 381 | Self { | 253 | // "Preceding reads and writes cannot be moved past subsequent writes." |
| 382 | tx: TDesRing::new(), | 254 | fence(Ordering::Release); |
| 383 | rx: RDesRing::new(), | 255 | |
| 384 | } | 256 | // signal DMA it can try again. |
| 385 | } | 257 | // NOTE(unsafe) Atomic write |
| 258 | unsafe { ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0) } | ||
| 386 | 259 | ||
| 387 | pub fn init(&mut self) { | 260 | // Increment index. |
| 388 | self.tx.init(); | 261 | self.index += 1; |
| 389 | self.rx.init(); | 262 | if self.index == self.descriptors.len() { |
| 263 | self.index = 0 | ||
| 264 | } | ||
| 390 | } | 265 | } |
| 391 | } | 266 | } |
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 5b76d1e7f..fcb4a296c 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -1,35 +1,28 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | mod descriptors; |
| 2 | |||
| 2 | use core::sync::atomic::{fence, Ordering}; | 3 | use core::sync::atomic::{fence, Ordering}; |
| 3 | use core::task::Waker; | ||
| 4 | 4 | ||
| 5 | use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; | 5 | use embassy_cortex_m::interrupt::InterruptExt; |
| 6 | use embassy_hal_common::{into_ref, PeripheralRef}; | 6 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 7 | use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU}; | ||
| 8 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 9 | 7 | ||
| 8 | pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; | ||
| 9 | use super::*; | ||
| 10 | use crate::gpio::sealed::{AFType, Pin as _}; | 10 | use crate::gpio::sealed::{AFType, Pin as _}; |
| 11 | use crate::gpio::{AnyPin, Speed}; | 11 | use crate::gpio::{AnyPin, Speed}; |
| 12 | use crate::pac::{ETH, RCC, SYSCFG}; | 12 | use crate::pac::{ETH, RCC, SYSCFG}; |
| 13 | use crate::Peripheral; | 13 | use crate::Peripheral; |
| 14 | 14 | ||
| 15 | mod descriptors; | 15 | const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet |
| 16 | use descriptors::DescriptorRing; | ||
| 17 | |||
| 18 | use super::*; | ||
| 19 | 16 | ||
| 20 | pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage<Inner<'d, T, TX, RX>>); | 17 | pub struct Ethernet<'d, T: Instance, P: PHY> { |
| 21 | impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { | 18 | _peri: PeripheralRef<'d, T>, |
| 22 | pub const fn new() -> Self { | 19 | pub(crate) tx: TDesRing<'d>, |
| 23 | Self(StateStorage::new()) | 20 | pub(crate) rx: RDesRing<'d>, |
| 24 | } | ||
| 25 | } | ||
| 26 | pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> { | ||
| 27 | state: PeripheralMutex<'d, Inner<'d, T, TX, RX>>, | ||
| 28 | pins: [PeripheralRef<'d, AnyPin>; 9], | 21 | pins: [PeripheralRef<'d, AnyPin>; 9], |
| 29 | _phy: P, | 22 | _phy: P, |
| 30 | clock_range: u8, | 23 | clock_range: u8, |
| 31 | phy_addr: u8, | 24 | phy_addr: u8, |
| 32 | mac_addr: [u8; 6], | 25 | pub(crate) mac_addr: [u8; 6], |
| 33 | } | 26 | } |
| 34 | 27 | ||
| 35 | macro_rules! config_pins { | 28 | macro_rules! config_pins { |
| @@ -44,10 +37,9 @@ macro_rules! config_pins { | |||
| 44 | }; | 37 | }; |
| 45 | } | 38 | } |
| 46 | 39 | ||
| 47 | impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> { | 40 | impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { |
| 48 | /// safety: the returned instance is not leak-safe | 41 | pub fn new<const TX: usize, const RX: usize>( |
| 49 | pub unsafe fn new( | 42 | queue: &'d mut PacketQueue<TX, RX>, |
| 50 | state: &'d mut State<'d, T, TX, RX>, | ||
| 51 | peri: impl Peripheral<P = T> + 'd, | 43 | peri: impl Peripheral<P = T> + 'd, |
| 52 | interrupt: impl Peripheral<P = crate::interrupt::ETH> + 'd, | 44 | interrupt: impl Peripheral<P = crate::interrupt::ETH> + 'd, |
| 53 | ref_clk: impl Peripheral<P = impl RefClkPin<T>> + 'd, | 45 | ref_clk: impl Peripheral<P = impl RefClkPin<T>> + 'd, |
| @@ -63,126 +55,123 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, | |||
| 63 | mac_addr: [u8; 6], | 55 | mac_addr: [u8; 6], |
| 64 | phy_addr: u8, | 56 | phy_addr: u8, |
| 65 | ) -> Self { | 57 | ) -> Self { |
| 66 | into_ref!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 58 | into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); |
| 67 | 59 | ||
| 68 | // Enable the necessary Clocks | 60 | unsafe { |
| 69 | // NOTE(unsafe) We have exclusive access to the registers | 61 | // Enable the necessary Clocks |
| 70 | critical_section::with(|_| { | 62 | // NOTE(unsafe) We have exclusive access to the registers |
| 71 | RCC.apb4enr().modify(|w| w.set_syscfgen(true)); | 63 | critical_section::with(|_| { |
| 72 | RCC.ahb1enr().modify(|w| { | 64 | RCC.apb4enr().modify(|w| w.set_syscfgen(true)); |
| 73 | w.set_eth1macen(true); | 65 | RCC.ahb1enr().modify(|w| { |
| 74 | w.set_eth1txen(true); | 66 | w.set_eth1macen(true); |
| 75 | w.set_eth1rxen(true); | 67 | w.set_eth1txen(true); |
| 68 | w.set_eth1rxen(true); | ||
| 69 | }); | ||
| 70 | |||
| 71 | // RMII | ||
| 72 | SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); | ||
| 76 | }); | 73 | }); |
| 77 | 74 | ||
| 78 | // RMII | 75 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); |
| 79 | SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); | 76 | |
| 80 | }); | 77 | // NOTE(unsafe) We have exclusive access to the registers |
| 81 | 78 | let dma = ETH.ethernet_dma(); | |
| 82 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 79 | let mac = ETH.ethernet_mac(); |
| 83 | 80 | let mtl = ETH.ethernet_mtl(); | |
| 84 | // NOTE(unsafe) We are ourselves not leak-safe. | 81 | |
| 85 | let state = PeripheralMutex::new(interrupt, &mut state.0, || Inner::new(peri)); | 82 | // Reset and wait |
| 86 | 83 | dma.dmamr().modify(|w| w.set_swr(true)); | |
| 87 | // NOTE(unsafe) We have exclusive access to the registers | 84 | while dma.dmamr().read().swr() {} |
| 88 | let dma = ETH.ethernet_dma(); | 85 | |
| 89 | let mac = ETH.ethernet_mac(); | 86 | mac.maccr().modify(|w| { |
| 90 | let mtl = ETH.ethernet_mtl(); | 87 | w.set_ipg(0b000); // 96 bit times |
| 91 | 88 | w.set_acs(true); | |
| 92 | // Reset and wait | 89 | w.set_fes(true); |
| 93 | dma.dmamr().modify(|w| w.set_swr(true)); | 90 | w.set_dm(true); |
| 94 | while dma.dmamr().read().swr() {} | 91 | // TODO: Carrier sense ? ECRSFD |
| 95 | 92 | }); | |
| 96 | mac.maccr().modify(|w| { | 93 | |
| 97 | w.set_ipg(0b000); // 96 bit times | 94 | // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, |
| 98 | w.set_acs(true); | 95 | // so the LR write must happen after the HR write. |
| 99 | w.set_fes(true); | 96 | mac.maca0hr() |
| 100 | w.set_dm(true); | 97 | .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); |
| 101 | // TODO: Carrier sense ? ECRSFD | 98 | mac.maca0lr().write(|w| { |
| 102 | }); | 99 | w.set_addrlo( |
| 103 | 100 | u32::from(mac_addr[0]) | |
| 104 | // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, | 101 | | (u32::from(mac_addr[1]) << 8) |
| 105 | // so the LR write must happen after the HR write. | 102 | | (u32::from(mac_addr[2]) << 16) |
| 106 | mac.maca0hr() | 103 | | (u32::from(mac_addr[3]) << 24), |
| 107 | .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); | 104 | ) |
| 108 | mac.maca0lr().write(|w| { | 105 | }); |
| 109 | w.set_addrlo( | 106 | |
| 110 | u32::from(mac_addr[0]) | 107 | mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); |
| 111 | | (u32::from(mac_addr[1]) << 8) | 108 | |
| 112 | | (u32::from(mac_addr[2]) << 16) | 109 | // disable all MMC RX interrupts |
| 113 | | (u32::from(mac_addr[3]) << 24), | 110 | mac.mmc_rx_interrupt_mask().write(|w| { |
| 114 | ) | 111 | w.set_rxcrcerpim(true); |
| 115 | }); | 112 | w.set_rxalgnerpim(true); |
| 116 | 113 | w.set_rxucgpim(true); | |
| 117 | mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); | 114 | w.set_rxlpiuscim(true); |
| 118 | 115 | w.set_rxlpitrcim(true) | |
| 119 | // disable all MMC RX interrupts | 116 | }); |
| 120 | mac.mmc_rx_interrupt_mask().write(|w| { | 117 | |
| 121 | w.set_rxcrcerpim(true); | 118 | // disable all MMC TX interrupts |
| 122 | w.set_rxalgnerpim(true); | 119 | mac.mmc_tx_interrupt_mask().write(|w| { |
| 123 | w.set_rxucgpim(true); | 120 | w.set_txscolgpim(true); |
| 124 | w.set_rxlpiuscim(true); | 121 | w.set_txmcolgpim(true); |
| 125 | w.set_rxlpitrcim(true) | 122 | w.set_txgpktim(true); |
| 126 | }); | 123 | w.set_txlpiuscim(true); |
| 127 | 124 | w.set_txlpitrcim(true); | |
| 128 | // disable all MMC TX interrupts | 125 | }); |
| 129 | mac.mmc_tx_interrupt_mask().write(|w| { | 126 | |
| 130 | w.set_txscolgpim(true); | 127 | mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); |
| 131 | w.set_txmcolgpim(true); | 128 | mtl.mtltx_qomr().modify(|w| w.set_tsf(true)); |
| 132 | w.set_txgpktim(true); | 129 | |
| 133 | w.set_txlpiuscim(true); | 130 | dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ? |
| 134 | w.set_txlpitrcim(true); | 131 | dma.dmacrx_cr().modify(|w| { |
| 135 | }); | 132 | w.set_rxpbl(1); // 32 ? |
| 136 | 133 | w.set_rbsz(MTU as u16); | |
| 137 | mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); | 134 | }); |
| 138 | mtl.mtltx_qomr().modify(|w| w.set_tsf(true)); | 135 | |
| 139 | 136 | // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called | |
| 140 | dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ? | 137 | let hclk = crate::rcc::get_freqs().ahb1; |
| 141 | dma.dmacrx_cr().modify(|w| { | 138 | let hclk_mhz = hclk.0 / 1_000_000; |
| 142 | w.set_rxpbl(1); // 32 ? | 139 | |
| 143 | w.set_rbsz(MTU as u16); | 140 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz |
| 144 | }); | 141 | let clock_range = match hclk_mhz { |
| 145 | 142 | 0..=34 => 2, // Divide by 16 | |
| 146 | // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called | 143 | 35..=59 => 3, // Divide by 26 |
| 147 | let hclk = crate::rcc::get_freqs().ahb1; | 144 | 60..=99 => 0, // Divide by 42 |
| 148 | let hclk_mhz = hclk.0 / 1_000_000; | 145 | 100..=149 => 1, // Divide by 62 |
| 149 | 146 | 150..=249 => 4, // Divide by 102 | |
| 150 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | 147 | 250..=310 => 5, // Divide by 124 |
| 151 | let clock_range = match hclk_mhz { | 148 | _ => { |
| 152 | 0..=34 => 2, // Divide by 16 | 149 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") |
| 153 | 35..=59 => 3, // Divide by 26 | 150 | } |
| 154 | 60..=99 => 0, // Divide by 42 | 151 | }; |
| 155 | 100..=149 => 1, // Divide by 62 | 152 | |
| 156 | 150..=249 => 4, // Divide by 102 | 153 | let pins = [ |
| 157 | 250..=310 => 5, // Divide by 124 | 154 | ref_clk.map_into(), |
| 158 | _ => { | 155 | mdio.map_into(), |
| 159 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | 156 | mdc.map_into(), |
| 160 | } | 157 | crs.map_into(), |
| 161 | }; | 158 | rx_d0.map_into(), |
| 162 | 159 | rx_d1.map_into(), | |
| 163 | let pins = [ | 160 | tx_d0.map_into(), |
| 164 | ref_clk.map_into(), | 161 | tx_d1.map_into(), |
| 165 | mdio.map_into(), | 162 | tx_en.map_into(), |
| 166 | mdc.map_into(), | 163 | ]; |
| 167 | crs.map_into(), | 164 | |
| 168 | rx_d0.map_into(), | 165 | let mut this = Self { |
| 169 | rx_d1.map_into(), | 166 | _peri: peri, |
| 170 | tx_d0.map_into(), | 167 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 171 | tx_d1.map_into(), | 168 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), |
| 172 | tx_en.map_into(), | 169 | pins, |
| 173 | ]; | 170 | _phy: phy, |
| 174 | 171 | clock_range, | |
| 175 | let mut this = Self { | 172 | phy_addr, |
| 176 | state, | 173 | mac_addr, |
| 177 | pins, | 174 | }; |
| 178 | _phy: phy, | ||
| 179 | clock_range, | ||
| 180 | phy_addr, | ||
| 181 | mac_addr, | ||
| 182 | }; | ||
| 183 | |||
| 184 | this.state.with(|s| { | ||
| 185 | s.desc_ring.init(); | ||
| 186 | 175 | ||
| 187 | fence(Ordering::SeqCst); | 176 | fence(Ordering::SeqCst); |
| 188 | 177 | ||
| @@ -205,17 +194,37 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, | |||
| 205 | w.set_rie(true); | 194 | w.set_rie(true); |
| 206 | w.set_tie(true); | 195 | w.set_tie(true); |
| 207 | }); | 196 | }); |
| 208 | }); | ||
| 209 | P::phy_reset(&mut this); | ||
| 210 | P::phy_init(&mut this); | ||
| 211 | 197 | ||
| 212 | this | 198 | P::phy_reset(&mut this); |
| 199 | P::phy_init(&mut this); | ||
| 200 | |||
| 201 | interrupt.set_handler(Self::on_interrupt); | ||
| 202 | interrupt.enable(); | ||
| 203 | |||
| 204 | this | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | fn on_interrupt(_cx: *mut ()) { | ||
| 209 | WAKER.wake(); | ||
| 210 | |||
| 211 | // TODO: Check and clear more flags | ||
| 212 | unsafe { | ||
| 213 | let dma = ETH.ethernet_dma(); | ||
| 214 | |||
| 215 | dma.dmacsr().modify(|w| { | ||
| 216 | w.set_ti(true); | ||
| 217 | w.set_ri(true); | ||
| 218 | w.set_nis(true); | ||
| 219 | }); | ||
| 220 | // Delay two peripheral's clock | ||
| 221 | dma.dmacsr().read(); | ||
| 222 | dma.dmacsr().read(); | ||
| 223 | } | ||
| 213 | } | 224 | } |
| 214 | } | 225 | } |
| 215 | 226 | ||
| 216 | unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement | 227 | unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { |
| 217 | for Ethernet<'d, T, P, TX, RX> | ||
| 218 | { | ||
| 219 | fn smi_read(&mut self, reg: u8) -> u16 { | 228 | fn smi_read(&mut self, reg: u8) -> u16 { |
| 220 | // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` | 229 | // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` |
| 221 | unsafe { | 230 | unsafe { |
| @@ -251,44 +260,7 @@ unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationMa | |||
| 251 | } | 260 | } |
| 252 | } | 261 | } |
| 253 | 262 | ||
| 254 | impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device for Ethernet<'d, T, P, TX, RX> { | 263 | impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { |
| 255 | fn is_transmit_ready(&mut self) -> bool { | ||
| 256 | self.state.with(|s| s.desc_ring.tx.available()) | ||
| 257 | } | ||
| 258 | |||
| 259 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 260 | self.state.with(|s| unwrap!(s.desc_ring.tx.transmit(pkt))); | ||
| 261 | } | ||
| 262 | |||
| 263 | fn receive(&mut self) -> Option<PacketBuf> { | ||
| 264 | self.state.with(|s| s.desc_ring.rx.pop_packet()) | ||
| 265 | } | ||
| 266 | |||
| 267 | fn register_waker(&mut self, waker: &Waker) { | ||
| 268 | WAKER.register(waker); | ||
| 269 | } | ||
| 270 | |||
| 271 | fn capabilities(&self) -> DeviceCapabilities { | ||
| 272 | let mut caps = DeviceCapabilities::default(); | ||
| 273 | caps.max_transmission_unit = MTU; | ||
| 274 | caps.max_burst_size = Some(TX.min(RX)); | ||
| 275 | caps | ||
| 276 | } | ||
| 277 | |||
| 278 | fn link_state(&mut self) -> LinkState { | ||
| 279 | if P::poll_link(self) { | ||
| 280 | LinkState::Up | ||
| 281 | } else { | ||
| 282 | LinkState::Down | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 287 | self.mac_addr | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, P, TX, RX> { | ||
| 292 | fn drop(&mut self) { | 264 | fn drop(&mut self) { |
| 293 | // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers | 265 | // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers |
| 294 | unsafe { | 266 | unsafe { |
| @@ -325,46 +297,3 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Etherne | |||
| 325 | }) | 297 | }) |
| 326 | } | 298 | } |
| 327 | } | 299 | } |
| 328 | |||
| 329 | //---------------------------------------------------------------------- | ||
| 330 | |||
| 331 | struct Inner<'d, T: Instance, const TX: usize, const RX: usize> { | ||
| 332 | _peri: PhantomData<&'d mut T>, | ||
| 333 | desc_ring: DescriptorRing<TX, RX>, | ||
| 334 | } | ||
| 335 | |||
| 336 | impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> { | ||
| 337 | pub fn new(_peri: impl Peripheral<P = T> + 'd) -> Self { | ||
| 338 | Self { | ||
| 339 | _peri: PhantomData, | ||
| 340 | desc_ring: DescriptorRing::new(), | ||
| 341 | } | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> { | ||
| 346 | type Interrupt = crate::interrupt::ETH; | ||
| 347 | |||
| 348 | fn on_interrupt(&mut self) { | ||
| 349 | unwrap!(self.desc_ring.tx.on_interrupt()); | ||
| 350 | self.desc_ring.rx.on_interrupt(); | ||
| 351 | |||
| 352 | WAKER.wake(); | ||
| 353 | |||
| 354 | // TODO: Check and clear more flags | ||
| 355 | unsafe { | ||
| 356 | let dma = ETH.ethernet_dma(); | ||
| 357 | |||
| 358 | dma.dmacsr().modify(|w| { | ||
| 359 | w.set_ti(true); | ||
| 360 | w.set_ri(true); | ||
| 361 | w.set_nis(true); | ||
| 362 | }); | ||
| 363 | // Delay two peripheral's clock | ||
| 364 | dma.dmacsr().read(); | ||
| 365 | dma.dmacsr().read(); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index b59ba8a22..1e72ce682 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml | |||
| @@ -19,6 +19,7 @@ default = ["usbd-hid"] | |||
| 19 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 19 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 20 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } | 20 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } |
| 21 | embassy-sync = { version = "0.1.0", path = "../embassy-sync" } | 21 | embassy-sync = { version = "0.1.0", path = "../embassy-sync" } |
| 22 | embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } | ||
| 22 | 23 | ||
| 23 | defmt = { version = "0.3", optional = true } | 24 | defmt = { version = "0.3", optional = true } |
| 24 | log = { version = "0.4.14", optional = true } | 25 | log = { version = "0.4.14", optional = true } |
diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs new file mode 100644 index 000000000..60bbfd8d4 --- /dev/null +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs | |||
| @@ -0,0 +1,449 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::mem::MaybeUninit; | ||
| 3 | use core::task::Context; | ||
| 4 | |||
| 5 | use embassy_futures::select::{select, Either}; | ||
| 6 | use embassy_net::device::{Device as DeviceTrait, DeviceCapabilities, LinkState, Medium}; | ||
| 7 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 8 | use embassy_sync::blocking_mutex::Mutex; | ||
| 9 | use embassy_sync::waitqueue::WakerRegistration; | ||
| 10 | use embassy_usb_driver::Driver; | ||
| 11 | |||
| 12 | use super::{CdcNcmClass, Receiver, Sender}; | ||
| 13 | |||
| 14 | pub struct State<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> { | ||
| 15 | rx: [PacketBuf<MTU>; N_RX], | ||
| 16 | tx: [PacketBuf<MTU>; N_TX], | ||
| 17 | inner: MaybeUninit<StateInner<'d, MTU>>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> State<'d, MTU, N_RX, N_TX> { | ||
| 21 | const NEW_PACKET: PacketBuf<MTU> = PacketBuf::new(); | ||
| 22 | |||
| 23 | pub const fn new() -> Self { | ||
| 24 | Self { | ||
| 25 | rx: [Self::NEW_PACKET; N_RX], | ||
| 26 | tx: [Self::NEW_PACKET; N_TX], | ||
| 27 | inner: MaybeUninit::uninit(), | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | struct StateInner<'d, const MTU: usize> { | ||
| 33 | rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>, | ||
| 34 | tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>, | ||
| 35 | link_state: Mutex<NoopRawMutex, RefCell<LinkStateState>>, | ||
| 36 | } | ||
| 37 | |||
| 38 | /// State of the LinkState | ||
| 39 | struct LinkStateState { | ||
| 40 | state: LinkState, | ||
| 41 | waker: WakerRegistration, | ||
| 42 | } | ||
| 43 | |||
| 44 | pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { | ||
| 45 | tx_usb: Sender<'d, D>, | ||
| 46 | tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>, | ||
| 47 | rx_usb: Receiver<'d, D>, | ||
| 48 | rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>, | ||
| 49 | link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>, | ||
| 50 | } | ||
| 51 | |||
| 52 | impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { | ||
| 53 | pub async fn run(mut self) -> ! { | ||
| 54 | let rx_fut = async move { | ||
| 55 | loop { | ||
| 56 | trace!("WAITING for connection"); | ||
| 57 | self.link_state.lock(|s| { | ||
| 58 | let s = &mut *s.borrow_mut(); | ||
| 59 | s.state = LinkState::Down; | ||
| 60 | s.waker.wake(); | ||
| 61 | }); | ||
| 62 | |||
| 63 | self.rx_usb.wait_connection().await.unwrap(); | ||
| 64 | |||
| 65 | trace!("Connected"); | ||
| 66 | self.link_state.lock(|s| { | ||
| 67 | let s = &mut *s.borrow_mut(); | ||
| 68 | s.state = LinkState::Up; | ||
| 69 | s.waker.wake(); | ||
| 70 | }); | ||
| 71 | |||
| 72 | loop { | ||
| 73 | let p = self.rx_chan.send().await; | ||
| 74 | match self.rx_usb.read_packet(&mut p.buf).await { | ||
| 75 | Ok(n) => { | ||
| 76 | p.len = n; | ||
| 77 | self.rx_chan.send_done(); | ||
| 78 | } | ||
| 79 | Err(e) => { | ||
| 80 | warn!("error reading packet: {:?}", e); | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | }; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | }; | ||
| 87 | let tx_fut = async move { | ||
| 88 | loop { | ||
| 89 | let p = self.tx_chan.recv().await; | ||
| 90 | if let Err(e) = self.tx_usb.write_packet(&p.buf[..p.len]).await { | ||
| 91 | warn!("Failed to TX packet: {:?}", e); | ||
| 92 | } | ||
| 93 | self.tx_chan.recv_done(); | ||
| 94 | } | ||
| 95 | }; | ||
| 96 | match select(rx_fut, tx_fut).await { | ||
| 97 | Either::First(x) => x, | ||
| 98 | Either::Second(x) => x, | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | ||
| 104 | pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>( | ||
| 105 | self, | ||
| 106 | state: &'d mut State<'d, MTU, N_RX, N_TX>, | ||
| 107 | ethernet_address: [u8; 6], | ||
| 108 | ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { | ||
| 109 | let (tx_usb, rx_usb) = self.split(); | ||
| 110 | |||
| 111 | let mut caps = DeviceCapabilities::default(); | ||
| 112 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | ||
| 113 | caps.medium = Medium::Ethernet; | ||
| 114 | |||
| 115 | let state = state.inner.write(StateInner { | ||
| 116 | rx: zerocopy_channel::Channel::new(&mut state.rx[..]), | ||
| 117 | tx: zerocopy_channel::Channel::new(&mut state.tx[..]), | ||
| 118 | link_state: Mutex::new(RefCell::new(LinkStateState { | ||
| 119 | state: LinkState::Down, | ||
| 120 | waker: WakerRegistration::new(), | ||
| 121 | })), | ||
| 122 | }); | ||
| 123 | |||
| 124 | let (rx_sender, rx_receiver) = state.rx.split(); | ||
| 125 | let (tx_sender, tx_receiver) = state.tx.split(); | ||
| 126 | |||
| 127 | ( | ||
| 128 | Runner { | ||
| 129 | tx_usb, | ||
| 130 | tx_chan: tx_receiver, | ||
| 131 | rx_usb, | ||
| 132 | rx_chan: rx_sender, | ||
| 133 | link_state: &state.link_state, | ||
| 134 | }, | ||
| 135 | Device { | ||
| 136 | caps, | ||
| 137 | ethernet_address, | ||
| 138 | link_state: &state.link_state, | ||
| 139 | rx: rx_receiver, | ||
| 140 | tx: tx_sender, | ||
| 141 | }, | ||
| 142 | ) | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | pub struct PacketBuf<const MTU: usize> { | ||
| 147 | len: usize, | ||
| 148 | buf: [u8; MTU], | ||
| 149 | } | ||
| 150 | |||
| 151 | impl<const MTU: usize> PacketBuf<MTU> { | ||
| 152 | pub const fn new() -> Self { | ||
| 153 | Self { len: 0, buf: [0; MTU] } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | pub struct Device<'d, const MTU: usize> { | ||
| 158 | rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>, | ||
| 159 | tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>, | ||
| 160 | link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>, | ||
| 161 | caps: DeviceCapabilities, | ||
| 162 | ethernet_address: [u8; 6], | ||
| 163 | } | ||
| 164 | |||
| 165 | impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> { | ||
| 166 | type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; | ||
| 167 | type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; | ||
| 168 | |||
| 169 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { | ||
| 170 | if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { | ||
| 171 | Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) | ||
| 172 | } else { | ||
| 173 | None | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Construct a transmit token. | ||
| 178 | fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> { | ||
| 179 | if self.tx.poll_send(cx).is_ready() { | ||
| 180 | Some(TxToken { tx: self.tx.borrow() }) | ||
| 181 | } else { | ||
| 182 | None | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Get a description of device capabilities. | ||
| 187 | fn capabilities(&self) -> DeviceCapabilities { | ||
| 188 | self.caps.clone() | ||
| 189 | } | ||
| 190 | |||
| 191 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 192 | self.ethernet_address | ||
| 193 | } | ||
| 194 | |||
| 195 | fn link_state(&mut self, cx: &mut Context) -> LinkState { | ||
| 196 | self.link_state.lock(|s| { | ||
| 197 | let s = &mut *s.borrow_mut(); | ||
| 198 | s.waker.register(cx.waker()); | ||
| 199 | s.state | ||
| 200 | }) | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | pub struct RxToken<'a, const MTU: usize> { | ||
| 205 | rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf<MTU>>, | ||
| 206 | } | ||
| 207 | |||
| 208 | impl<'a, const MTU: usize> embassy_net::device::RxToken for RxToken<'a, MTU> { | ||
| 209 | fn consume<R, F>(mut self, f: F) -> R | ||
| 210 | where | ||
| 211 | F: FnOnce(&mut [u8]) -> R, | ||
| 212 | { | ||
| 213 | // NOTE(unwrap): we checked the queue wasn't full when creating the token. | ||
| 214 | let pkt = unwrap!(self.rx.try_recv()); | ||
| 215 | let r = f(&mut pkt.buf[..pkt.len]); | ||
| 216 | self.rx.recv_done(); | ||
| 217 | r | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | pub struct TxToken<'a, const MTU: usize> { | ||
| 222 | tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf<MTU>>, | ||
| 223 | } | ||
| 224 | |||
| 225 | impl<'a, const MTU: usize> embassy_net::device::TxToken for TxToken<'a, MTU> { | ||
| 226 | fn consume<R, F>(mut self, len: usize, f: F) -> R | ||
| 227 | where | ||
| 228 | F: FnOnce(&mut [u8]) -> R, | ||
| 229 | { | ||
| 230 | // NOTE(unwrap): we checked the queue wasn't full when creating the token. | ||
| 231 | let pkt = unwrap!(self.tx.try_send()); | ||
| 232 | let r = f(&mut pkt.buf[..len]); | ||
| 233 | pkt.len = len; | ||
| 234 | self.tx.send_done(); | ||
| 235 | r | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | mod zerocopy_channel { | ||
| 240 | use core::cell::RefCell; | ||
| 241 | use core::future::poll_fn; | ||
| 242 | use core::marker::PhantomData; | ||
| 243 | use core::task::{Context, Poll}; | ||
| 244 | |||
| 245 | use embassy_sync::blocking_mutex::raw::RawMutex; | ||
| 246 | use embassy_sync::blocking_mutex::Mutex; | ||
| 247 | use embassy_sync::waitqueue::WakerRegistration; | ||
| 248 | |||
| 249 | pub struct Channel<'a, M: RawMutex, T> { | ||
| 250 | buf: *mut T, | ||
| 251 | phantom: PhantomData<&'a mut T>, | ||
| 252 | state: Mutex<M, RefCell<State>>, | ||
| 253 | } | ||
| 254 | |||
| 255 | impl<'a, M: RawMutex, T> Channel<'a, M, T> { | ||
| 256 | pub fn new(buf: &'a mut [T]) -> Self { | ||
| 257 | let len = buf.len(); | ||
| 258 | assert!(len != 0); | ||
| 259 | |||
| 260 | Self { | ||
| 261 | buf: buf.as_mut_ptr(), | ||
| 262 | phantom: PhantomData, | ||
| 263 | state: Mutex::new(RefCell::new(State { | ||
| 264 | len, | ||
| 265 | front: 0, | ||
| 266 | back: 0, | ||
| 267 | full: false, | ||
| 268 | send_waker: WakerRegistration::new(), | ||
| 269 | recv_waker: WakerRegistration::new(), | ||
| 270 | })), | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { | ||
| 275 | (Sender { channel: self }, Receiver { channel: self }) | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | pub struct Sender<'a, M: RawMutex, T> { | ||
| 280 | channel: &'a Channel<'a, M, T>, | ||
| 281 | } | ||
| 282 | |||
| 283 | impl<'a, M: RawMutex, T> Sender<'a, M, T> { | ||
| 284 | pub fn borrow(&mut self) -> Sender<'_, M, T> { | ||
| 285 | Sender { channel: self.channel } | ||
| 286 | } | ||
| 287 | |||
| 288 | pub fn try_send(&mut self) -> Option<&mut T> { | ||
| 289 | self.channel.state.lock(|s| { | ||
| 290 | let s = &mut *s.borrow_mut(); | ||
| 291 | match s.push_index() { | ||
| 292 | Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), | ||
| 293 | None => None, | ||
| 294 | } | ||
| 295 | }) | ||
| 296 | } | ||
| 297 | |||
| 298 | pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { | ||
| 299 | self.channel.state.lock(|s| { | ||
| 300 | let s = &mut *s.borrow_mut(); | ||
| 301 | match s.push_index() { | ||
| 302 | Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), | ||
| 303 | None => { | ||
| 304 | s.recv_waker.register(cx.waker()); | ||
| 305 | Poll::Pending | ||
| 306 | } | ||
| 307 | } | ||
| 308 | }) | ||
| 309 | } | ||
| 310 | |||
| 311 | pub async fn send(&mut self) -> &mut T { | ||
| 312 | let i = poll_fn(|cx| { | ||
| 313 | self.channel.state.lock(|s| { | ||
| 314 | let s = &mut *s.borrow_mut(); | ||
| 315 | match s.push_index() { | ||
| 316 | Some(i) => Poll::Ready(i), | ||
| 317 | None => { | ||
| 318 | s.recv_waker.register(cx.waker()); | ||
| 319 | Poll::Pending | ||
| 320 | } | ||
| 321 | } | ||
| 322 | }) | ||
| 323 | }) | ||
| 324 | .await; | ||
| 325 | unsafe { &mut *self.channel.buf.add(i) } | ||
| 326 | } | ||
| 327 | |||
| 328 | pub fn send_done(&mut self) { | ||
| 329 | self.channel.state.lock(|s| s.borrow_mut().push_done()) | ||
| 330 | } | ||
| 331 | } | ||
| 332 | pub struct Receiver<'a, M: RawMutex, T> { | ||
| 333 | channel: &'a Channel<'a, M, T>, | ||
| 334 | } | ||
| 335 | |||
| 336 | impl<'a, M: RawMutex, T> Receiver<'a, M, T> { | ||
| 337 | pub fn borrow(&mut self) -> Receiver<'_, M, T> { | ||
| 338 | Receiver { channel: self.channel } | ||
| 339 | } | ||
| 340 | |||
| 341 | pub fn try_recv(&mut self) -> Option<&mut T> { | ||
| 342 | self.channel.state.lock(|s| { | ||
| 343 | let s = &mut *s.borrow_mut(); | ||
| 344 | match s.pop_index() { | ||
| 345 | Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), | ||
| 346 | None => None, | ||
| 347 | } | ||
| 348 | }) | ||
| 349 | } | ||
| 350 | |||
| 351 | pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { | ||
| 352 | self.channel.state.lock(|s| { | ||
| 353 | let s = &mut *s.borrow_mut(); | ||
| 354 | match s.pop_index() { | ||
| 355 | Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), | ||
| 356 | None => { | ||
| 357 | s.send_waker.register(cx.waker()); | ||
| 358 | Poll::Pending | ||
| 359 | } | ||
| 360 | } | ||
| 361 | }) | ||
| 362 | } | ||
| 363 | |||
| 364 | pub async fn recv(&mut self) -> &mut T { | ||
| 365 | let i = poll_fn(|cx| { | ||
| 366 | self.channel.state.lock(|s| { | ||
| 367 | let s = &mut *s.borrow_mut(); | ||
| 368 | match s.pop_index() { | ||
| 369 | Some(i) => Poll::Ready(i), | ||
| 370 | None => { | ||
| 371 | s.send_waker.register(cx.waker()); | ||
| 372 | Poll::Pending | ||
| 373 | } | ||
| 374 | } | ||
| 375 | }) | ||
| 376 | }) | ||
| 377 | .await; | ||
| 378 | unsafe { &mut *self.channel.buf.add(i) } | ||
| 379 | } | ||
| 380 | |||
| 381 | pub fn recv_done(&mut self) { | ||
| 382 | self.channel.state.lock(|s| s.borrow_mut().pop_done()) | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | struct State { | ||
| 387 | len: usize, | ||
| 388 | |||
| 389 | /// Front index. Always 0..=(N-1) | ||
| 390 | front: usize, | ||
| 391 | /// Back index. Always 0..=(N-1). | ||
| 392 | back: usize, | ||
| 393 | |||
| 394 | /// Used to distinguish "empty" and "full" cases when `front == back`. | ||
| 395 | /// May only be `true` if `front == back`, always `false` otherwise. | ||
| 396 | full: bool, | ||
| 397 | |||
| 398 | send_waker: WakerRegistration, | ||
| 399 | recv_waker: WakerRegistration, | ||
| 400 | } | ||
| 401 | |||
| 402 | impl State { | ||
| 403 | fn increment(&self, i: usize) -> usize { | ||
| 404 | if i + 1 == self.len { | ||
| 405 | 0 | ||
| 406 | } else { | ||
| 407 | i + 1 | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | fn is_full(&self) -> bool { | ||
| 412 | self.full | ||
| 413 | } | ||
| 414 | |||
| 415 | fn is_empty(&self) -> bool { | ||
| 416 | self.front == self.back && !self.full | ||
| 417 | } | ||
| 418 | |||
| 419 | fn push_index(&mut self) -> Option<usize> { | ||
| 420 | match self.is_full() { | ||
| 421 | true => None, | ||
| 422 | false => Some(self.back), | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 426 | fn push_done(&mut self) { | ||
| 427 | assert!(!self.is_full()); | ||
| 428 | self.back = self.increment(self.back); | ||
| 429 | if self.back == self.front { | ||
| 430 | self.full = true; | ||
| 431 | } | ||
| 432 | self.send_waker.wake(); | ||
| 433 | } | ||
| 434 | |||
| 435 | fn pop_index(&mut self) -> Option<usize> { | ||
| 436 | match self.is_empty() { | ||
| 437 | true => None, | ||
| 438 | false => Some(self.front), | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | fn pop_done(&mut self) { | ||
| 443 | assert!(!self.is_empty()); | ||
| 444 | self.front = self.increment(self.front); | ||
| 445 | self.full = false; | ||
| 446 | self.recv_waker.wake(); | ||
| 447 | } | ||
| 448 | } | ||
| 449 | } | ||
diff --git a/embassy-usb/src/class/cdc_ncm.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index a39b87e9b..2ee47f68c 100644 --- a/embassy-usb/src/class/cdc_ncm.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs | |||
| @@ -1,3 +1,18 @@ | |||
| 1 | /// CDC-NCM, aka Ethernet over USB. | ||
| 2 | /// | ||
| 3 | /// # Compatibility | ||
| 4 | /// | ||
| 5 | /// Windows: NOT supported in Windows 10. Supported in Windows 11. | ||
| 6 | /// | ||
| 7 | /// Linux: Well-supported since forever. | ||
| 8 | /// | ||
| 9 | /// Android: Support for CDC-NCM is spotty and varies across manufacturers. | ||
| 10 | /// | ||
| 11 | /// - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||
| 12 | /// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||
| 13 | /// it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||
| 14 | /// This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 | ||
| 15 | /// and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||
| 1 | use core::intrinsics::copy_nonoverlapping; | 16 | use core::intrinsics::copy_nonoverlapping; |
| 2 | use core::mem::{size_of, MaybeUninit}; | 17 | use core::mem::{size_of, MaybeUninit}; |
| 3 | 18 | ||
| @@ -6,6 +21,9 @@ use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; | |||
| 6 | use crate::types::*; | 21 | use crate::types::*; |
| 7 | use crate::Builder; | 22 | use crate::Builder; |
| 8 | 23 | ||
| 24 | #[cfg(feature = "embassy-net")] | ||
| 25 | pub mod embassy_net; | ||
| 26 | |||
| 9 | /// This should be used as `device_class` when building the `UsbDevice`. | 27 | /// This should be used as `device_class` when building the `UsbDevice`. |
| 10 | pub const USB_CLASS_CDC: u8 = 0x02; | 28 | pub const USB_CLASS_CDC: u8 = 0x02; |
| 11 | 29 | ||
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 8b95ac3a6..a980a505c 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml | |||
| @@ -16,7 +16,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature | |||
| 16 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 16 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 17 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 17 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 18 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } | 18 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } |
| 19 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } | 19 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"], optional = true } |
| 20 | embedded-io = "0.4.0" | 20 | embedded-io = "0.4.0" |
| 21 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } | 21 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } |
| 22 | 22 | ||
diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index de93a2b45..e5f704524 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs | |||
| @@ -3,19 +3,16 @@ | |||
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use core::mem; | 5 | use core::mem; |
| 6 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 7 | use core::task::Waker; | ||
| 8 | 6 | ||
| 9 | use defmt::*; | 7 | use defmt::*; |
| 10 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 11 | use embassy_net::tcp::TcpSocket; | 9 | use embassy_net::tcp::TcpSocket; |
| 12 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; | 10 | use embassy_net::{Stack, StackResources}; |
| 13 | use embassy_nrf::rng::Rng; | 11 | use embassy_nrf::rng::Rng; |
| 14 | use embassy_nrf::usb::{Driver, PowerUsb}; | 12 | use embassy_nrf::usb::{Driver, PowerUsb}; |
| 15 | use embassy_nrf::{interrupt, pac, peripherals}; | 13 | use embassy_nrf::{interrupt, pac, peripherals}; |
| 16 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 14 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; |
| 17 | use embassy_sync::channel::Channel; | 15 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; |
| 18 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; | ||
| 19 | use embassy_usb::{Builder, Config, UsbDevice}; | 16 | use embassy_usb::{Builder, Config, UsbDevice}; |
| 20 | use embedded_io::asynch::Write; | 17 | use embedded_io::asynch::Write; |
| 21 | use static_cell::StaticCell; | 18 | use static_cell::StaticCell; |
| @@ -27,56 +24,25 @@ macro_rules! singleton { | |||
| 27 | ($val:expr) => {{ | 24 | ($val:expr) => {{ |
| 28 | type T = impl Sized; | 25 | type T = impl Sized; |
| 29 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 26 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 30 | STATIC_CELL.init_with(move || $val) | 27 | let (x,) = STATIC_CELL.init(($val,)); |
| 28 | x | ||
| 31 | }}; | 29 | }}; |
| 32 | } | 30 | } |
| 33 | 31 | ||
| 32 | const MTU: usize = 1514; | ||
| 33 | |||
| 34 | #[embassy_executor::task] | 34 | #[embassy_executor::task] |
| 35 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | 35 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { |
| 36 | device.run().await | 36 | device.run().await |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | #[embassy_executor::task] | 39 | #[embassy_executor::task] |
| 40 | async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { | 40 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { |
| 41 | loop { | 41 | class.run().await |
| 42 | warn!("WAITING for connection"); | ||
| 43 | LINK_UP.store(false, Ordering::Relaxed); | ||
| 44 | |||
| 45 | class.wait_connection().await.unwrap(); | ||
| 46 | |||
| 47 | warn!("Connected"); | ||
| 48 | LINK_UP.store(true, Ordering::Relaxed); | ||
| 49 | |||
| 50 | loop { | ||
| 51 | let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); | ||
| 52 | let n = match class.read_packet(&mut p[..]).await { | ||
| 53 | Ok(n) => n, | ||
| 54 | Err(e) => { | ||
| 55 | warn!("error reading packet: {:?}", e); | ||
| 56 | break; | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | |||
| 60 | let buf = p.slice(0..n); | ||
| 61 | if RX_CHANNEL.try_send(buf).is_err() { | ||
| 62 | warn!("Failed pushing rx'd packet to channel."); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | #[embassy_executor::task] | ||
| 69 | async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { | ||
| 70 | loop { | ||
| 71 | let pkt = TX_CHANNEL.recv().await; | ||
| 72 | if let Err(e) = class.write_packet(&pkt[..]).await { | ||
| 73 | warn!("Failed to TX packet: {:?}", e); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | 42 | } |
| 77 | 43 | ||
| 78 | #[embassy_executor::task] | 44 | #[embassy_executor::task] |
| 79 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 45 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { |
| 80 | stack.run().await | 46 | stack.run().await |
| 81 | } | 47 | } |
| 82 | 48 | ||
| @@ -108,55 +74,32 @@ async fn main(spawner: Spawner) { | |||
| 108 | config.device_sub_class = 0x02; | 74 | config.device_sub_class = 0x02; |
| 109 | config.device_protocol = 0x01; | 75 | config.device_protocol = 0x01; |
| 110 | 76 | ||
| 111 | struct Resources { | ||
| 112 | device_descriptor: [u8; 256], | ||
| 113 | config_descriptor: [u8; 256], | ||
| 114 | bos_descriptor: [u8; 256], | ||
| 115 | control_buf: [u8; 128], | ||
| 116 | serial_state: State<'static>, | ||
| 117 | } | ||
| 118 | let res: &mut Resources = singleton!(Resources { | ||
| 119 | device_descriptor: [0; 256], | ||
| 120 | config_descriptor: [0; 256], | ||
| 121 | bos_descriptor: [0; 256], | ||
| 122 | control_buf: [0; 128], | ||
| 123 | serial_state: State::new(), | ||
| 124 | }); | ||
| 125 | |||
| 126 | // Create embassy-usb DeviceBuilder using the driver and config. | 77 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 127 | let mut builder = Builder::new( | 78 | let mut builder = Builder::new( |
| 128 | driver, | 79 | driver, |
| 129 | config, | 80 | config, |
| 130 | &mut res.device_descriptor, | 81 | &mut singleton!([0; 256])[..], |
| 131 | &mut res.config_descriptor, | 82 | &mut singleton!([0; 256])[..], |
| 132 | &mut res.bos_descriptor, | 83 | &mut singleton!([0; 256])[..], |
| 133 | &mut res.control_buf, | 84 | &mut singleton!([0; 128])[..], |
| 134 | None, | 85 | None, |
| 135 | ); | 86 | ); |
| 136 | 87 | ||
| 137 | // WARNINGS for Android ethernet tethering: | ||
| 138 | // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||
| 139 | // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||
| 140 | // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||
| 141 | // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 | ||
| 142 | // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||
| 143 | |||
| 144 | // Our MAC addr. | 88 | // Our MAC addr. |
| 145 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | 89 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; |
| 146 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | 90 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. |
| 147 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | 91 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; |
| 148 | 92 | ||
| 149 | // Create classes on the builder. | 93 | // Create classes on the builder. |
| 150 | let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); | 94 | let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); |
| 151 | 95 | ||
| 152 | // Build the builder. | 96 | // Build the builder. |
| 153 | let usb = builder.build(); | 97 | let usb = builder.build(); |
| 154 | 98 | ||
| 155 | unwrap!(spawner.spawn(usb_task(usb))); | 99 | unwrap!(spawner.spawn(usb_task(usb))); |
| 156 | 100 | ||
| 157 | let (tx, rx) = class.split(); | 101 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(singleton!(NetState::new()), our_mac_addr); |
| 158 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); | 102 | unwrap!(spawner.spawn(usb_ncm_task(runner))); |
| 159 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); | ||
| 160 | 103 | ||
| 161 | let config = embassy_net::ConfigStrategy::Dhcp; | 104 | let config = embassy_net::ConfigStrategy::Dhcp; |
| 162 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | 105 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { |
| @@ -172,7 +115,6 @@ async fn main(spawner: Spawner) { | |||
| 172 | let seed = u64::from_le_bytes(seed); | 115 | let seed = u64::from_le_bytes(seed); |
| 173 | 116 | ||
| 174 | // Init network stack | 117 | // Init network stack |
| 175 | let device = Device { mac_addr: our_mac_addr }; | ||
| 176 | let stack = &*singleton!(Stack::new( | 118 | let stack = &*singleton!(Stack::new( |
| 177 | device, | 119 | device, |
| 178 | config, | 120 | config, |
| @@ -225,50 +167,3 @@ async fn main(spawner: Spawner) { | |||
| 225 | } | 167 | } |
| 226 | } | 168 | } |
| 227 | } | 169 | } |
| 228 | |||
| 229 | static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 230 | static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 231 | static LINK_UP: AtomicBool = AtomicBool::new(false); | ||
| 232 | |||
| 233 | struct Device { | ||
| 234 | mac_addr: [u8; 6], | ||
| 235 | } | ||
| 236 | |||
| 237 | impl embassy_net::Device for Device { | ||
| 238 | fn register_waker(&mut self, waker: &Waker) { | ||
| 239 | // loopy loopy wakey wakey | ||
| 240 | waker.wake_by_ref() | ||
| 241 | } | ||
| 242 | |||
| 243 | fn link_state(&mut self) -> embassy_net::LinkState { | ||
| 244 | match LINK_UP.load(Ordering::Relaxed) { | ||
| 245 | true => embassy_net::LinkState::Up, | ||
| 246 | false => embassy_net::LinkState::Down, | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | fn capabilities(&self) -> embassy_net::DeviceCapabilities { | ||
| 251 | let mut caps = embassy_net::DeviceCapabilities::default(); | ||
| 252 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | ||
| 253 | caps.medium = embassy_net::Medium::Ethernet; | ||
| 254 | caps | ||
| 255 | } | ||
| 256 | |||
| 257 | fn is_transmit_ready(&mut self) -> bool { | ||
| 258 | true | ||
| 259 | } | ||
| 260 | |||
| 261 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 262 | if TX_CHANNEL.try_send(pkt).is_err() { | ||
| 263 | warn!("TX failed") | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | fn receive<'a>(&mut self) -> Option<PacketBuf> { | ||
| 268 | RX_CHANNEL.try_recv().ok() | ||
| 269 | } | ||
| 270 | |||
| 271 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 272 | self.mac_addr | ||
| 273 | } | ||
| 274 | } | ||
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index dfbc26426..31a08cfb6 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml | |||
| @@ -10,7 +10,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de | |||
| 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } | 12 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } |
| 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } |
| 14 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | 14 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } | 16 | embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } |
diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 1057fe7fd..d0aec874a 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs | |||
| @@ -2,18 +2,14 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 6 | use core::task::Waker; | ||
| 7 | |||
| 8 | use defmt::*; | 5 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 10 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 11 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; | 8 | use embassy_net::{Stack, StackResources}; |
| 12 | use embassy_rp::usb::Driver; | 9 | use embassy_rp::usb::Driver; |
| 13 | use embassy_rp::{interrupt, peripherals}; | 10 | use embassy_rp::{interrupt, peripherals}; |
| 14 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 11 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; |
| 15 | use embassy_sync::channel::Channel; | 12 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; |
| 16 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; | ||
| 17 | use embassy_usb::{Builder, Config, UsbDevice}; | 13 | use embassy_usb::{Builder, Config, UsbDevice}; |
| 18 | use embedded_io::asynch::Write; | 14 | use embedded_io::asynch::Write; |
| 19 | use static_cell::StaticCell; | 15 | use static_cell::StaticCell; |
| @@ -25,56 +21,25 @@ macro_rules! singleton { | |||
| 25 | ($val:expr) => {{ | 21 | ($val:expr) => {{ |
| 26 | type T = impl Sized; | 22 | type T = impl Sized; |
| 27 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 23 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 28 | STATIC_CELL.init_with(move || $val) | 24 | let (x,) = STATIC_CELL.init(($val,)); |
| 25 | x | ||
| 29 | }}; | 26 | }}; |
| 30 | } | 27 | } |
| 31 | 28 | ||
| 29 | const MTU: usize = 1514; | ||
| 30 | |||
| 32 | #[embassy_executor::task] | 31 | #[embassy_executor::task] |
| 33 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | 32 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { |
| 34 | device.run().await | 33 | device.run().await |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | #[embassy_executor::task] | 36 | #[embassy_executor::task] |
| 38 | async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { | 37 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { |
| 39 | loop { | 38 | class.run().await |
| 40 | warn!("WAITING for connection"); | ||
| 41 | LINK_UP.store(false, Ordering::Relaxed); | ||
| 42 | |||
| 43 | class.wait_connection().await.unwrap(); | ||
| 44 | |||
| 45 | warn!("Connected"); | ||
| 46 | LINK_UP.store(true, Ordering::Relaxed); | ||
| 47 | |||
| 48 | loop { | ||
| 49 | let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); | ||
| 50 | let n = match class.read_packet(&mut p[..]).await { | ||
| 51 | Ok(n) => n, | ||
| 52 | Err(e) => { | ||
| 53 | warn!("error reading packet: {:?}", e); | ||
| 54 | break; | ||
| 55 | } | ||
| 56 | }; | ||
| 57 | |||
| 58 | let buf = p.slice(0..n); | ||
| 59 | if RX_CHANNEL.try_send(buf).is_err() { | ||
| 60 | warn!("Failed pushing rx'd packet to channel."); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | #[embassy_executor::task] | ||
| 67 | async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { | ||
| 68 | loop { | ||
| 69 | let pkt = TX_CHANNEL.recv().await; | ||
| 70 | if let Err(e) = class.write_packet(&pkt[..]).await { | ||
| 71 | warn!("Failed to TX packet: {:?}", e); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | 39 | } |
| 75 | 40 | ||
| 76 | #[embassy_executor::task] | 41 | #[embassy_executor::task] |
| 77 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 42 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { |
| 78 | stack.run().await | 43 | stack.run().await |
| 79 | } | 44 | } |
| 80 | 45 | ||
| @@ -100,55 +65,32 @@ async fn main(spawner: Spawner) { | |||
| 100 | config.device_sub_class = 0x02; | 65 | config.device_sub_class = 0x02; |
| 101 | config.device_protocol = 0x01; | 66 | config.device_protocol = 0x01; |
| 102 | 67 | ||
| 103 | struct Resources { | ||
| 104 | device_descriptor: [u8; 256], | ||
| 105 | config_descriptor: [u8; 256], | ||
| 106 | bos_descriptor: [u8; 256], | ||
| 107 | control_buf: [u8; 128], | ||
| 108 | serial_state: State<'static>, | ||
| 109 | } | ||
| 110 | let res: &mut Resources = singleton!(Resources { | ||
| 111 | device_descriptor: [0; 256], | ||
| 112 | config_descriptor: [0; 256], | ||
| 113 | bos_descriptor: [0; 256], | ||
| 114 | control_buf: [0; 128], | ||
| 115 | serial_state: State::new(), | ||
| 116 | }); | ||
| 117 | |||
| 118 | // Create embassy-usb DeviceBuilder using the driver and config. | 68 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 119 | let mut builder = Builder::new( | 69 | let mut builder = Builder::new( |
| 120 | driver, | 70 | driver, |
| 121 | config, | 71 | config, |
| 122 | &mut res.device_descriptor, | 72 | &mut singleton!([0; 256])[..], |
| 123 | &mut res.config_descriptor, | 73 | &mut singleton!([0; 256])[..], |
| 124 | &mut res.bos_descriptor, | 74 | &mut singleton!([0; 256])[..], |
| 125 | &mut res.control_buf, | 75 | &mut singleton!([0; 128])[..], |
| 126 | None, | 76 | None, |
| 127 | ); | 77 | ); |
| 128 | 78 | ||
| 129 | // WARNINGS for Android ethernet tethering: | ||
| 130 | // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||
| 131 | // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||
| 132 | // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||
| 133 | // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 | ||
| 134 | // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||
| 135 | |||
| 136 | // Our MAC addr. | 79 | // Our MAC addr. |
| 137 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | 80 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; |
| 138 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | 81 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. |
| 139 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | 82 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; |
| 140 | 83 | ||
| 141 | // Create classes on the builder. | 84 | // Create classes on the builder. |
| 142 | let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); | 85 | let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); |
| 143 | 86 | ||
| 144 | // Build the builder. | 87 | // Build the builder. |
| 145 | let usb = builder.build(); | 88 | let usb = builder.build(); |
| 146 | 89 | ||
| 147 | unwrap!(spawner.spawn(usb_task(usb))); | 90 | unwrap!(spawner.spawn(usb_task(usb))); |
| 148 | 91 | ||
| 149 | let (tx, rx) = class.split(); | 92 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(singleton!(NetState::new()), our_mac_addr); |
| 150 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); | 93 | unwrap!(spawner.spawn(usb_ncm_task(runner))); |
| 151 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); | ||
| 152 | 94 | ||
| 153 | let config = embassy_net::ConfigStrategy::Dhcp; | 95 | let config = embassy_net::ConfigStrategy::Dhcp; |
| 154 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | 96 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { |
| @@ -161,7 +103,6 @@ async fn main(spawner: Spawner) { | |||
| 161 | let seed = 1234; // guaranteed random, chosen by a fair dice roll | 103 | let seed = 1234; // guaranteed random, chosen by a fair dice roll |
| 162 | 104 | ||
| 163 | // Init network stack | 105 | // Init network stack |
| 164 | let device = Device { mac_addr: our_mac_addr }; | ||
| 165 | let stack = &*singleton!(Stack::new( | 106 | let stack = &*singleton!(Stack::new( |
| 166 | device, | 107 | device, |
| 167 | config, | 108 | config, |
| @@ -214,50 +155,3 @@ async fn main(spawner: Spawner) { | |||
| 214 | } | 155 | } |
| 215 | } | 156 | } |
| 216 | } | 157 | } |
| 217 | |||
| 218 | static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 219 | static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 220 | static LINK_UP: AtomicBool = AtomicBool::new(false); | ||
| 221 | |||
| 222 | struct Device { | ||
| 223 | mac_addr: [u8; 6], | ||
| 224 | } | ||
| 225 | |||
| 226 | impl embassy_net::Device for Device { | ||
| 227 | fn register_waker(&mut self, waker: &Waker) { | ||
| 228 | // loopy loopy wakey wakey | ||
| 229 | waker.wake_by_ref() | ||
| 230 | } | ||
| 231 | |||
| 232 | fn link_state(&mut self) -> embassy_net::LinkState { | ||
| 233 | match LINK_UP.load(Ordering::Relaxed) { | ||
| 234 | true => embassy_net::LinkState::Up, | ||
| 235 | false => embassy_net::LinkState::Down, | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | fn capabilities(&self) -> embassy_net::DeviceCapabilities { | ||
| 240 | let mut caps = embassy_net::DeviceCapabilities::default(); | ||
| 241 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | ||
| 242 | caps.medium = embassy_net::Medium::Ethernet; | ||
| 243 | caps | ||
| 244 | } | ||
| 245 | |||
| 246 | fn is_transmit_ready(&mut self) -> bool { | ||
| 247 | true | ||
| 248 | } | ||
| 249 | |||
| 250 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 251 | if TX_CHANNEL.try_send(pkt).is_err() { | ||
| 252 | warn!("TX failed") | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | fn receive<'a>(&mut self) -> Option<PacketBuf> { | ||
| 257 | RX_CHANNEL.try_recv().ok() | ||
| 258 | } | ||
| 259 | |||
| 260 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 261 | self.mac_addr | ||
| 262 | } | ||
| 263 | } | ||
diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs index a0cace7f7..bb3e194cc 100644 --- a/examples/std/src/tuntap.rs +++ b/examples/std/src/tuntap.rs | |||
| @@ -1,8 +1,10 @@ | |||
| 1 | use std::io; | 1 | use std::io; |
| 2 | use std::io::{Read, Write}; | 2 | use std::io::{Read, Write}; |
| 3 | use std::os::unix::io::{AsRawFd, RawFd}; | 3 | use std::os::unix::io::{AsRawFd, RawFd}; |
| 4 | use std::task::Context; | ||
| 4 | 5 | ||
| 5 | use async_io::Async; | 6 | use async_io::Async; |
| 7 | use embassy_net::device::{self, Device, DeviceCapabilities, LinkState}; | ||
| 6 | use log::*; | 8 | use log::*; |
| 7 | 9 | ||
| 8 | pub const SIOCGIFMTU: libc::c_ulong = 0x8921; | 10 | pub const SIOCGIFMTU: libc::c_ulong = 0x8921; |
| @@ -125,54 +127,35 @@ impl io::Write for TunTap { | |||
| 125 | 127 | ||
| 126 | pub struct TunTapDevice { | 128 | pub struct TunTapDevice { |
| 127 | device: Async<TunTap>, | 129 | device: Async<TunTap>, |
| 128 | waker: Option<Waker>, | ||
| 129 | } | 130 | } |
| 130 | 131 | ||
| 131 | impl TunTapDevice { | 132 | impl TunTapDevice { |
| 132 | pub fn new(name: &str) -> io::Result<TunTapDevice> { | 133 | pub fn new(name: &str) -> io::Result<TunTapDevice> { |
| 133 | Ok(Self { | 134 | Ok(Self { |
| 134 | device: Async::new(TunTap::new(name)?)?, | 135 | device: Async::new(TunTap::new(name)?)?, |
| 135 | waker: None, | ||
| 136 | }) | 136 | }) |
| 137 | } | 137 | } |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | use core::task::Waker; | ||
| 141 | use std::task::Context; | ||
| 142 | |||
| 143 | use embassy_net::{Device, DeviceCapabilities, LinkState, Packet, PacketBox, PacketBoxExt, PacketBuf}; | ||
| 144 | |||
| 145 | impl Device for TunTapDevice { | 140 | impl Device for TunTapDevice { |
| 146 | fn is_transmit_ready(&mut self) -> bool { | 141 | type RxToken<'a> = RxToken where Self: 'a; |
| 147 | true | 142 | type TxToken<'a> = TxToken<'a> where Self: 'a; |
| 148 | } | ||
| 149 | 143 | ||
| 150 | fn transmit(&mut self, pkt: PacketBuf) { | 144 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { |
| 151 | // todo handle WouldBlock | 145 | let mut buf = vec![0; self.device.get_ref().mtu]; |
| 152 | match self.device.get_mut().write(&pkt) { | ||
| 153 | Ok(_) => {} | ||
| 154 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { | ||
| 155 | info!("transmit WouldBlock"); | ||
| 156 | } | ||
| 157 | Err(e) => panic!("transmit error: {:?}", e), | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | fn receive(&mut self) -> Option<PacketBuf> { | ||
| 162 | let mut pkt = PacketBox::new(Packet::new()).unwrap(); | ||
| 163 | loop { | 146 | loop { |
| 164 | match self.device.get_mut().read(&mut pkt[..]) { | 147 | match self.device.get_mut().read(&mut buf) { |
| 165 | Ok(n) => { | 148 | Ok(n) => { |
| 166 | return Some(pkt.slice(0..n)); | 149 | buf.truncate(n); |
| 150 | return Some(( | ||
| 151 | RxToken { buffer: buf }, | ||
| 152 | TxToken { | ||
| 153 | device: &mut self.device, | ||
| 154 | }, | ||
| 155 | )); | ||
| 167 | } | 156 | } |
| 168 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { | 157 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { |
| 169 | let ready = if let Some(w) = self.waker.as_ref() { | 158 | if !self.device.poll_readable(cx).is_ready() { |
| 170 | let mut cx = Context::from_waker(w); | ||
| 171 | self.device.poll_readable(&mut cx).is_ready() | ||
| 172 | } else { | ||
| 173 | false | ||
| 174 | }; | ||
| 175 | if !ready { | ||
| 176 | return None; | 159 | return None; |
| 177 | } | 160 | } |
| 178 | } | 161 | } |
| @@ -181,28 +164,10 @@ impl Device for TunTapDevice { | |||
| 181 | } | 164 | } |
| 182 | } | 165 | } |
| 183 | 166 | ||
| 184 | fn register_waker(&mut self, w: &Waker) { | 167 | fn transmit(&mut self, _cx: &mut Context) -> Option<Self::TxToken<'_>> { |
| 185 | match self.waker { | 168 | Some(TxToken { |
| 186 | // Optimization: If both the old and new Wakers wake the same task, we can simply | 169 | device: &mut self.device, |
| 187 | // keep the old waker, skipping the clone. (In most executor implementations, | 170 | }) |
| 188 | // cloning a waker is somewhat expensive, comparable to cloning an Arc). | ||
| 189 | Some(ref w2) if (w2.will_wake(w)) => {} | ||
| 190 | _ => { | ||
| 191 | // clone the new waker and store it | ||
| 192 | if let Some(old_waker) = core::mem::replace(&mut self.waker, Some(w.clone())) { | ||
| 193 | // We had a waker registered for another task. Wake it, so the other task can | ||
| 194 | // reregister itself if it's still interested. | ||
| 195 | // | ||
| 196 | // If two tasks are waiting on the same thing concurrently, this will cause them | ||
| 197 | // to wake each other in a loop fighting over this WakerRegistration. This wastes | ||
| 198 | // CPU but things will still work. | ||
| 199 | // | ||
| 200 | // If the user wants to have two tasks waiting on the same thing they should use | ||
| 201 | // a more appropriate primitive that can store multiple wakers. | ||
| 202 | old_waker.wake() | ||
| 203 | } | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | 171 | } |
| 207 | 172 | ||
| 208 | fn capabilities(&self) -> DeviceCapabilities { | 173 | fn capabilities(&self) -> DeviceCapabilities { |
| @@ -211,7 +176,7 @@ impl Device for TunTapDevice { | |||
| 211 | caps | 176 | caps |
| 212 | } | 177 | } |
| 213 | 178 | ||
| 214 | fn link_state(&mut self) -> LinkState { | 179 | fn link_state(&mut self, _cx: &mut Context) -> LinkState { |
| 215 | LinkState::Up | 180 | LinkState::Up |
| 216 | } | 181 | } |
| 217 | 182 | ||
| @@ -219,3 +184,41 @@ impl Device for TunTapDevice { | |||
| 219 | [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] | 184 | [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] |
| 220 | } | 185 | } |
| 221 | } | 186 | } |
| 187 | |||
| 188 | #[doc(hidden)] | ||
| 189 | pub struct RxToken { | ||
| 190 | buffer: Vec<u8>, | ||
| 191 | } | ||
| 192 | |||
| 193 | impl device::RxToken for RxToken { | ||
| 194 | fn consume<R, F>(mut self, f: F) -> R | ||
| 195 | where | ||
| 196 | F: FnOnce(&mut [u8]) -> R, | ||
| 197 | { | ||
| 198 | f(&mut self.buffer) | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | #[doc(hidden)] | ||
| 203 | pub struct TxToken<'a> { | ||
| 204 | device: &'a mut Async<TunTap>, | ||
| 205 | } | ||
| 206 | |||
| 207 | impl<'a> device::TxToken for TxToken<'a> { | ||
| 208 | fn consume<R, F>(self, len: usize, f: F) -> R | ||
| 209 | where | ||
| 210 | F: FnOnce(&mut [u8]) -> R, | ||
| 211 | { | ||
| 212 | let mut buffer = vec![0; len]; | ||
| 213 | let result = f(&mut buffer); | ||
| 214 | |||
| 215 | // todo handle WouldBlock with async | ||
| 216 | match self.device.get_mut().write(&buffer) { | ||
| 217 | Ok(_) => {} | ||
| 218 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => info!("transmit WouldBlock"), | ||
| 219 | Err(e) => panic!("transmit error: {:?}", e), | ||
| 220 | } | ||
| 221 | |||
| 222 | result | ||
| 223 | } | ||
| 224 | } | ||
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index f4d674cdc..afdf87000 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml | |||
| @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" | |||
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "embassy-net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } |
| 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } |
| 13 | embedded-io = { version = "0.4.0", features = ["async"] } | 13 | embedded-io = { version = "0.4.0", features = ["async"] } |
| 14 | 14 | ||
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 5202edf62..224cc202b 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs | |||
| @@ -7,7 +7,7 @@ use embassy_executor::Spawner; | |||
| 7 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; | 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; |
| 9 | use embassy_stm32::eth::generic_smi::GenericSMI; | 9 | use embassy_stm32::eth::generic_smi::GenericSMI; |
| 10 | use embassy_stm32::eth::{Ethernet, State}; | 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; |
| 11 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::ETH; |
| 12 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 13 | use embassy_stm32::time::mhz; | 13 | use embassy_stm32::time::mhz; |
| @@ -22,11 +22,12 @@ macro_rules! singleton { | |||
| 22 | ($val:expr) => {{ | 22 | ($val:expr) => {{ |
| 23 | type T = impl Sized; | 23 | type T = impl Sized; |
| 24 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 24 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 25 | STATIC_CELL.init_with(move || $val) | 25 | let (x,) = STATIC_CELL.init(($val,)); |
| 26 | x | ||
| 26 | }}; | 27 | }}; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; | 30 | type Device = Ethernet<'static, ETH, GenericSMI>; |
| 30 | 31 | ||
| 31 | #[embassy_executor::task] | 32 | #[embassy_executor::task] |
| 32 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 33 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| @@ -50,25 +51,23 @@ async fn main(spawner: Spawner) -> ! { | |||
| 50 | let eth_int = interrupt::take!(ETH); | 51 | let eth_int = interrupt::take!(ETH); |
| 51 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 52 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 52 | 53 | ||
| 53 | let device = unsafe { | 54 | let device = Ethernet::new( |
| 54 | Ethernet::new( | 55 | singleton!(PacketQueue::<16, 16>::new()), |
| 55 | singleton!(State::new()), | 56 | p.ETH, |
| 56 | p.ETH, | 57 | eth_int, |
| 57 | eth_int, | 58 | p.PA1, |
| 58 | p.PA1, | 59 | p.PA2, |
| 59 | p.PA2, | 60 | p.PC1, |
| 60 | p.PC1, | 61 | p.PA7, |
| 61 | p.PA7, | 62 | p.PC4, |
| 62 | p.PC4, | 63 | p.PC5, |
| 63 | p.PC5, | 64 | p.PG13, |
| 64 | p.PG13, | 65 | p.PB13, |
| 65 | p.PB13, | 66 | p.PG11, |
| 66 | p.PG11, | 67 | GenericSMI, |
| 67 | GenericSMI, | 68 | mac_addr, |
| 68 | mac_addr, | 69 | 0, |
| 69 | 0, | 70 | ); |
| 70 | ) | ||
| 71 | }; | ||
| 72 | 71 | ||
| 73 | let config = embassy_net::ConfigStrategy::Dhcp; | 72 | let config = embassy_net::ConfigStrategy::Dhcp; |
| 74 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | 73 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { |
| @@ -91,8 +90,8 @@ async fn main(spawner: Spawner) -> ! { | |||
| 91 | info!("Network task initialized"); | 90 | info!("Network task initialized"); |
| 92 | 91 | ||
| 93 | // Then we can use it! | 92 | // Then we can use it! |
| 94 | let mut rx_buffer = [0; 1024]; | 93 | let mut rx_buffer = [0; 4096]; |
| 95 | let mut tx_buffer = [0; 1024]; | 94 | let mut tx_buffer = [0; 4096]; |
| 96 | 95 | ||
| 97 | loop { | 96 | loop { |
| 98 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | 97 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); |
| @@ -107,8 +106,9 @@ async fn main(spawner: Spawner) -> ! { | |||
| 107 | continue; | 106 | continue; |
| 108 | } | 107 | } |
| 109 | info!("connected!"); | 108 | info!("connected!"); |
| 109 | let buf = [0; 1024]; | ||
| 110 | loop { | 110 | loop { |
| 111 | let r = socket.write_all(b"Hello\n").await; | 111 | let r = socket.write_all(&buf).await; |
| 112 | if let Err(e) = r { | 112 | if let Err(e) = r { |
| 113 | info!("write error: {:?}", e); | 113 | info!("write error: {:?}", e); |
| 114 | return; | 114 | return; |
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 11ce35053..5acf0035d 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml | |||
| @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" | |||
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "embassy-net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } |
| 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } | 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } |
| 13 | embedded-io = { version = "0.4.0", features = ["async"] } | 13 | embedded-io = { version = "0.4.0", features = ["async"] } |
| 14 | 14 | ||
diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 4ccc0b5ef..551325ca4 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs | |||
| @@ -7,7 +7,7 @@ use embassy_executor::Spawner; | |||
| 7 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; | 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; |
| 9 | use embassy_stm32::eth::generic_smi::GenericSMI; | 9 | use embassy_stm32::eth::generic_smi::GenericSMI; |
| 10 | use embassy_stm32::eth::{Ethernet, State}; | 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; |
| 11 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::ETH; |
| 12 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 13 | use embassy_stm32::time::mhz; | 13 | use embassy_stm32::time::mhz; |
| @@ -22,11 +22,12 @@ macro_rules! singleton { | |||
| 22 | ($val:expr) => {{ | 22 | ($val:expr) => {{ |
| 23 | type T = impl Sized; | 23 | type T = impl Sized; |
| 24 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 24 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 25 | STATIC_CELL.init_with(move || $val) | 25 | let (x,) = STATIC_CELL.init(($val,)); |
| 26 | x | ||
| 26 | }}; | 27 | }}; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; | 30 | type Device = Ethernet<'static, ETH, GenericSMI>; |
| 30 | 31 | ||
| 31 | #[embassy_executor::task] | 32 | #[embassy_executor::task] |
| 32 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 33 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| @@ -51,25 +52,23 @@ async fn main(spawner: Spawner) -> ! { | |||
| 51 | let eth_int = interrupt::take!(ETH); | 52 | let eth_int = interrupt::take!(ETH); |
| 52 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 53 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 53 | 54 | ||
| 54 | let device = unsafe { | 55 | let device = Ethernet::new( |
| 55 | Ethernet::new( | 56 | singleton!(PacketQueue::<16, 16>::new()), |
| 56 | singleton!(State::new()), | 57 | p.ETH, |
| 57 | p.ETH, | 58 | eth_int, |
| 58 | eth_int, | 59 | p.PA1, |
| 59 | p.PA1, | 60 | p.PA2, |
| 60 | p.PA2, | 61 | p.PC1, |
| 61 | p.PC1, | 62 | p.PA7, |
| 62 | p.PA7, | 63 | p.PC4, |
| 63 | p.PC4, | 64 | p.PC5, |
| 64 | p.PC5, | 65 | p.PG13, |
| 65 | p.PG13, | 66 | p.PB13, |
| 66 | p.PB13, | 67 | p.PG11, |
| 67 | p.PG11, | 68 | GenericSMI, |
| 68 | GenericSMI, | 69 | mac_addr, |
| 69 | mac_addr, | 70 | 0, |
| 70 | 0, | 71 | ); |
| 71 | ) | ||
| 72 | }; | ||
| 73 | 72 | ||
| 74 | let config = embassy_net::ConfigStrategy::Dhcp; | 73 | let config = embassy_net::ConfigStrategy::Dhcp; |
| 75 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | 74 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { |
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 64fd84141..61a08ae10 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs | |||
| @@ -7,7 +7,7 @@ use embassy_executor::Spawner; | |||
| 7 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; | 7 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; |
| 8 | use embassy_net::{Stack, StackResources}; | 8 | use embassy_net::{Stack, StackResources}; |
| 9 | use embassy_stm32::eth::generic_smi::GenericSMI; | 9 | use embassy_stm32::eth::generic_smi::GenericSMI; |
| 10 | use embassy_stm32::eth::{Ethernet, State}; | 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; |
| 11 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::ETH; |
| 12 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 13 | use embassy_stm32::time::mhz; | 13 | use embassy_stm32::time::mhz; |
| @@ -23,11 +23,12 @@ macro_rules! singleton { | |||
| 23 | ($val:expr) => {{ | 23 | ($val:expr) => {{ |
| 24 | type T = impl Sized; | 24 | type T = impl Sized; |
| 25 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 25 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 26 | STATIC_CELL.init_with(move || $val) | 26 | let (x,) = STATIC_CELL.init(($val,)); |
| 27 | x | ||
| 27 | }}; | 28 | }}; |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; | 31 | type Device = Ethernet<'static, ETH, GenericSMI>; |
| 31 | 32 | ||
| 32 | #[embassy_executor::task] | 33 | #[embassy_executor::task] |
| 33 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 34 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| @@ -52,25 +53,23 @@ async fn main(spawner: Spawner) -> ! { | |||
| 52 | let eth_int = interrupt::take!(ETH); | 53 | let eth_int = interrupt::take!(ETH); |
| 53 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 54 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 54 | 55 | ||
| 55 | let device = unsafe { | 56 | let device = Ethernet::new( |
| 56 | Ethernet::new( | 57 | singleton!(PacketQueue::<16, 16>::new()), |
| 57 | singleton!(State::new()), | 58 | p.ETH, |
| 58 | p.ETH, | 59 | eth_int, |
| 59 | eth_int, | 60 | p.PA1, |
| 60 | p.PA1, | 61 | p.PA2, |
| 61 | p.PA2, | 62 | p.PC1, |
| 62 | p.PC1, | 63 | p.PA7, |
| 63 | p.PA7, | 64 | p.PC4, |
| 64 | p.PC4, | 65 | p.PC5, |
| 65 | p.PC5, | 66 | p.PG13, |
| 66 | p.PG13, | 67 | p.PB13, |
| 67 | p.PB13, | 68 | p.PG11, |
| 68 | p.PG11, | 69 | GenericSMI, |
| 69 | GenericSMI, | 70 | mac_addr, |
| 70 | mac_addr, | 71 | 0, |
| 71 | 0, | 72 | ); |
| 72 | ) | ||
| 73 | }; | ||
| 74 | 73 | ||
| 75 | let config = embassy_net::ConfigStrategy::Dhcp; | 74 | let config = embassy_net::ConfigStrategy::Dhcp; |
| 76 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | 75 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { |
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 73ad50787..bb8515312 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml | |||
| @@ -11,7 +11,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de | |||
| 11 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 12 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } | 13 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } |
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } |
| 15 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | 15 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 17 | usbd-hid = "0.6.0" | 17 | usbd-hid = "0.6.0" |
diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 4f36d3f5a..b49329ea4 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs | |||
| @@ -2,20 +2,16 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 6 | use core::task::Waker; | ||
| 7 | |||
| 8 | use defmt::*; | 5 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 10 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 11 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; | 8 | use embassy_net::{Stack, StackResources}; |
| 12 | use embassy_stm32::rcc::*; | 9 | use embassy_stm32::rcc::*; |
| 13 | use embassy_stm32::rng::Rng; | 10 | use embassy_stm32::rng::Rng; |
| 14 | use embassy_stm32::usb::Driver; | 11 | use embassy_stm32::usb::Driver; |
| 15 | use embassy_stm32::{interrupt, Config}; | 12 | use embassy_stm32::{interrupt, Config}; |
| 16 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 13 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; |
| 17 | use embassy_sync::channel::Channel; | 14 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; |
| 18 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; | ||
| 19 | use embassy_usb::{Builder, UsbDevice}; | 15 | use embassy_usb::{Builder, UsbDevice}; |
| 20 | use embedded_io::asynch::Write; | 16 | use embedded_io::asynch::Write; |
| 21 | use rand_core::RngCore; | 17 | use rand_core::RngCore; |
| @@ -28,56 +24,25 @@ macro_rules! singleton { | |||
| 28 | ($val:expr) => {{ | 24 | ($val:expr) => {{ |
| 29 | type T = impl Sized; | 25 | type T = impl Sized; |
| 30 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 26 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 31 | STATIC_CELL.init_with(move || $val) | 27 | let (x,) = STATIC_CELL.init(($val,)); |
| 28 | x | ||
| 32 | }}; | 29 | }}; |
| 33 | } | 30 | } |
| 34 | 31 | ||
| 32 | const MTU: usize = 1514; | ||
| 33 | |||
| 35 | #[embassy_executor::task] | 34 | #[embassy_executor::task] |
| 36 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | 35 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { |
| 37 | device.run().await | 36 | device.run().await |
| 38 | } | 37 | } |
| 39 | 38 | ||
| 40 | #[embassy_executor::task] | 39 | #[embassy_executor::task] |
| 41 | async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { | 40 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { |
| 42 | loop { | 41 | class.run().await |
| 43 | warn!("WAITING for connection"); | ||
| 44 | LINK_UP.store(false, Ordering::Relaxed); | ||
| 45 | |||
| 46 | class.wait_connection().await.unwrap(); | ||
| 47 | |||
| 48 | warn!("Connected"); | ||
| 49 | LINK_UP.store(true, Ordering::Relaxed); | ||
| 50 | |||
| 51 | loop { | ||
| 52 | let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); | ||
| 53 | let n = match class.read_packet(&mut p[..]).await { | ||
| 54 | Ok(n) => n, | ||
| 55 | Err(e) => { | ||
| 56 | warn!("error reading packet: {:?}", e); | ||
| 57 | break; | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | |||
| 61 | let buf = p.slice(0..n); | ||
| 62 | if RX_CHANNEL.try_send(buf).is_err() { | ||
| 63 | warn!("Failed pushing rx'd packet to channel."); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | #[embassy_executor::task] | ||
| 70 | async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { | ||
| 71 | loop { | ||
| 72 | let pkt = TX_CHANNEL.recv().await; | ||
| 73 | if let Err(e) = class.write_packet(&pkt[..]).await { | ||
| 74 | warn!("Failed to TX packet: {:?}", e); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | } | 42 | } |
| 78 | 43 | ||
| 79 | #[embassy_executor::task] | 44 | #[embassy_executor::task] |
| 80 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 45 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { |
| 81 | stack.run().await | 46 | stack.run().await |
| 82 | } | 47 | } |
| 83 | 48 | ||
| @@ -106,55 +71,32 @@ async fn main(spawner: Spawner) { | |||
| 106 | config.device_sub_class = 0x02; | 71 | config.device_sub_class = 0x02; |
| 107 | config.device_protocol = 0x01; | 72 | config.device_protocol = 0x01; |
| 108 | 73 | ||
| 109 | struct Resources { | ||
| 110 | device_descriptor: [u8; 256], | ||
| 111 | config_descriptor: [u8; 256], | ||
| 112 | bos_descriptor: [u8; 256], | ||
| 113 | control_buf: [u8; 128], | ||
| 114 | serial_state: State<'static>, | ||
| 115 | } | ||
| 116 | let res: &mut Resources = singleton!(Resources { | ||
| 117 | device_descriptor: [0; 256], | ||
| 118 | config_descriptor: [0; 256], | ||
| 119 | bos_descriptor: [0; 256], | ||
| 120 | control_buf: [0; 128], | ||
| 121 | serial_state: State::new(), | ||
| 122 | }); | ||
| 123 | |||
| 124 | // Create embassy-usb DeviceBuilder using the driver and config. | 74 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 125 | let mut builder = Builder::new( | 75 | let mut builder = Builder::new( |
| 126 | driver, | 76 | driver, |
| 127 | config, | 77 | config, |
| 128 | &mut res.device_descriptor, | 78 | &mut singleton!([0; 256])[..], |
| 129 | &mut res.config_descriptor, | 79 | &mut singleton!([0; 256])[..], |
| 130 | &mut res.bos_descriptor, | 80 | &mut singleton!([0; 256])[..], |
| 131 | &mut res.control_buf, | 81 | &mut singleton!([0; 128])[..], |
| 132 | None, | 82 | None, |
| 133 | ); | 83 | ); |
| 134 | 84 | ||
| 135 | // WARNINGS for Android ethernet tethering: | ||
| 136 | // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||
| 137 | // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||
| 138 | // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||
| 139 | // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 | ||
| 140 | // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||
| 141 | |||
| 142 | // Our MAC addr. | 85 | // Our MAC addr. |
| 143 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | 86 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; |
| 144 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | 87 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. |
| 145 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | 88 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; |
| 146 | 89 | ||
| 147 | // Create classes on the builder. | 90 | // Create classes on the builder. |
| 148 | let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); | 91 | let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); |
| 149 | 92 | ||
| 150 | // Build the builder. | 93 | // Build the builder. |
| 151 | let usb = builder.build(); | 94 | let usb = builder.build(); |
| 152 | 95 | ||
| 153 | unwrap!(spawner.spawn(usb_task(usb))); | 96 | unwrap!(spawner.spawn(usb_task(usb))); |
| 154 | 97 | ||
| 155 | let (tx, rx) = class.split(); | 98 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(singleton!(NetState::new()), our_mac_addr); |
| 156 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); | 99 | unwrap!(spawner.spawn(usb_ncm_task(runner))); |
| 157 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); | ||
| 158 | 100 | ||
| 159 | let config = embassy_net::ConfigStrategy::Dhcp; | 101 | let config = embassy_net::ConfigStrategy::Dhcp; |
| 160 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | 102 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { |
| @@ -168,7 +110,6 @@ async fn main(spawner: Spawner) { | |||
| 168 | let seed = rng.next_u64(); | 110 | let seed = rng.next_u64(); |
| 169 | 111 | ||
| 170 | // Init network stack | 112 | // Init network stack |
| 171 | let device = Device { mac_addr: our_mac_addr }; | ||
| 172 | let stack = &*singleton!(Stack::new( | 113 | let stack = &*singleton!(Stack::new( |
| 173 | device, | 114 | device, |
| 174 | config, | 115 | config, |
| @@ -221,50 +162,3 @@ async fn main(spawner: Spawner) { | |||
| 221 | } | 162 | } |
| 222 | } | 163 | } |
| 223 | } | 164 | } |
| 224 | |||
| 225 | static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 226 | static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 227 | static LINK_UP: AtomicBool = AtomicBool::new(false); | ||
| 228 | |||
| 229 | struct Device { | ||
| 230 | mac_addr: [u8; 6], | ||
| 231 | } | ||
| 232 | |||
| 233 | impl embassy_net::Device for Device { | ||
| 234 | fn register_waker(&mut self, waker: &Waker) { | ||
| 235 | // loopy loopy wakey wakey | ||
| 236 | waker.wake_by_ref() | ||
| 237 | } | ||
| 238 | |||
| 239 | fn link_state(&mut self) -> embassy_net::LinkState { | ||
| 240 | match LINK_UP.load(Ordering::Relaxed) { | ||
| 241 | true => embassy_net::LinkState::Up, | ||
| 242 | false => embassy_net::LinkState::Down, | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | fn capabilities(&self) -> embassy_net::DeviceCapabilities { | ||
| 247 | let mut caps = embassy_net::DeviceCapabilities::default(); | ||
| 248 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | ||
| 249 | caps.medium = embassy_net::Medium::Ethernet; | ||
| 250 | caps | ||
| 251 | } | ||
| 252 | |||
| 253 | fn is_transmit_ready(&mut self) -> bool { | ||
| 254 | true | ||
| 255 | } | ||
| 256 | |||
| 257 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 258 | if TX_CHANNEL.try_send(pkt).is_err() { | ||
| 259 | warn!("TX failed") | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | fn receive<'a>(&mut self) -> Option<PacketBuf> { | ||
| 264 | RX_CHANNEL.try_recv().ok() | ||
| 265 | } | ||
| 266 | |||
| 267 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 268 | self.mac_addr | ||
| 269 | } | ||
| 270 | } | ||
