diff --git a/Cargo.toml b/Cargo.toml index e75b1f1..35c31d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,8 +39,15 @@ serde-json-core = "0.1" sfkv = "0.1" [patch.crates-io] -stm32f4xx-hal = { git = "https://github.com/stm32-rs/stm32f4xx-hal.git" } +# smoltcp = { path = "../smoltcp" } +# # TODO: pending https://github.com/stm32-rs/stm32f4xx-hal/pull/125 +# stm32f4xx-hal = { git = "https://github.com/thalesfragoso/stm32f4xx-hal", branch = "pwm-impl" } +#stm32f4xx-hal = { git = "https://github.com/stm32-rs/stm32f4xx-hal.git" } +stm32f4xx-hal = { path = "../../stm32f4/stm32f4xx-hal" } +sfkv = { path = "../sfkv" } +# [patch."https://github.com/stm32-rs/stm32-eth.git"] +# stm32-eth = { path = "../../stm32f4/stm32-eth" } [features] semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] diff --git a/src/command_parser.rs b/src/command_parser.rs index 6a1f649..e92e69a 100644 --- a/src/command_parser.rs +++ b/src/command_parser.rs @@ -85,6 +85,13 @@ impl fmt::Display for Error { } } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Ipv4Config { + pub address: [u8; 4], + pub mask_len: u8, + pub gateway: Option<[u8; 4]>, +} + #[derive(Debug, Clone, PartialEq)] pub enum ShowCommand { Input, @@ -139,7 +146,7 @@ pub enum Command { channel: Option, }, Reset, - Ipv4([u8; 4]), + Ipv4(Ipv4Config), Show(ShowCommand), Reporting(bool), /// PWM parameter setting @@ -473,9 +480,7 @@ fn save(input: &[u8]) -> IResult<&[u8], Result> { Ok((input, result)) } -fn ipv4(input: &[u8]) -> IResult<&[u8], Result> { - let (input, _) = tag("ipv4")(input)?; - let (input, _) = whitespace(input)?; +fn ipv4_addr(input: &[u8]) -> IResult<&[u8], Result<[u8; 4], Error>> { let (input, a) = unsigned(input)?; let (input, _) = tag(".")(input)?; let (input, b) = unsigned(input)?; @@ -483,12 +488,33 @@ fn ipv4(input: &[u8]) -> IResult<&[u8], Result> { let (input, c) = unsigned(input)?; let (input, _) = tag(".")(input)?; let (input, d) = unsigned(input)?; - end(input)?; + let address = move || Ok([a? as u8, b? as u8, c? as u8, d? as u8]); + Ok((input, address())) +} - let result = a.and_then(|a| b.and_then(|b| c.and_then(|c| d.map(|d| - Command::Ipv4([a as u8, b as u8, c as u8, d as u8]) - )))); - Ok((input, result)) +fn ipv4(input: &[u8]) -> IResult<&[u8], Result> { + let (input, _) = tag("ipv4")(input)?; + let (input, _) = whitespace(input)?; + let (input, address) = ipv4_addr(input)?; + let (input, _) = tag("/")(input)?; + let (input, mask_len) = unsigned(input)?; + let (input, gateway) = alt(( + |input| { + let (input, _) = whitespace(input)?; + let (input, gateway) = ipv4_addr(input)?; + Ok((input, gateway.map(Some))) + }, + value(Ok(None), end), + ))(input)?; + + let result = move || { + Ok(Command::Ipv4(Ipv4Config { + address: address?, + mask_len: mask_len? as u8, + gateway: gateway?, + })) + }; + Ok((input, result())) } fn command(input: &[u8]) -> IResult<&[u8], Result> { @@ -555,8 +581,22 @@ mod test { #[test] fn parse_ipv4() { - let command = Command::parse(b"ipv4 192.168.1.26"); - assert_eq!(command, Ok(Command::Ipv4([192, 168, 1, 26]))); + let command = Command::parse(b"ipv4 192.168.1.26/24"); + assert_eq!(command, Ok(Command::Ipv4(Ipv4Config { + address: [192, 168, 1, 26], + mask_len: 24, + gateway: None, + }))); + } + + #[test] + fn parse_ipv4_and_gateway() { + let command = Command::parse(b"ipv4 10.42.0.126/8 10.1.0.1"); + assert_eq!(command, Ok(Command::Ipv4(Ipv4Config { + address: [10, 42, 0, 126], + mask_len: 8, + gateway: Some([10, 1, 0, 1]), + }))); } #[test] diff --git a/src/config.rs b/src/config.rs index 9c77b2f..a1ca7ee 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,4 @@ use serde::{Serialize, Deserialize}; -use smoltcp::wire::Ipv4Address; use uom::si::{ electric_potential::volt, electric_current::ampere, diff --git a/src/main.rs b/src/main.rs index 9e8c214..340799a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,14 +17,14 @@ use cortex_m_rt::entry; use stm32f4xx_hal::{ hal::watchdog::{WatchdogEnable, Watchdog}, rcc::RccExt, - watchdog::IndependentWatchdog, - time::{U32Ext, MegaHertz}, stm32::{CorePeripherals, Peripherals, SCB}, + time::{U32Ext, MegaHertz}, + watchdog::IndependentWatchdog, }; use smoltcp::{ time::Instant, socket::TcpSocket, - wire::{EthernetAddress, Ipv4Address}, + wire::EthernetAddress, }; use uom::{ si::{ @@ -55,7 +55,7 @@ use server::Server; mod session; use session::{Session, SessionInput}; mod command_parser; -use command_parser::{Command, ShowCommand, PwmPin}; +use command_parser::{Command, Ipv4Config, PwmPin, ShowCommand}; mod timer; mod pid; mod steinhart_hart; @@ -75,7 +75,6 @@ const WATCHDOG_INTERVAL: u32 = 30_000; const CHANNEL_CONFIG_KEY: [&str; 2] = ["ch0", "ch1"]; -pub const DEFAULT_IPV4_ADDRESS: Ipv4Address = Ipv4Address([192, 168, 1, 26]); const TCP_PORT: u16 = 23; @@ -174,8 +173,19 @@ fn main() -> ! { } } - let mut ipv4_address = DEFAULT_IPV4_ADDRESS; - info!("IPv4 address: {}", ipv4_address); + // default net config: + let mut ipv4_config = Ipv4Config { + address: [192, 168, 1, 26], + mask_len: 24, + gateway: None, + }; + match store.read_value("ipv4") { + Ok(Some(config)) => + ipv4_config = config, + Ok(None) => {} + Err(e) => + error!("cannot read ipv4 config: {:?}", e), + } // EEPROM ships with a read-only EUI-48 identifier let mut eui48 = [0; 6]; @@ -183,12 +193,12 @@ fn main() -> ! { let hwaddr = EthernetAddress(eui48); info!("EEPROM MAC address: {}", hwaddr); - net::run(clocks, dp.ETHERNET_MAC, dp.ETHERNET_DMA, eth_pins, hwaddr, ipv4_address, |iface| { - let mut new_ipv4_address = None; + net::run(clocks, dp.ETHERNET_MAC, dp.ETHERNET_DMA, eth_pins, hwaddr, ipv4_config, |iface| { Server::::run(iface, |server| { leds.r1.off(); loop { + let mut new_ipv4_config = None; let instant = Instant::from_millis(i64::from(timer::now())); let updated_channel = channels.poll_adc(instant); if let Some(channel) = updated_channel { @@ -368,14 +378,15 @@ fn main() -> ! { let config = ChannelConfig::new(&mut channels, c); let _ = store .write_value(CHANNEL_CONFIG_KEY[c], &config, &mut store_value_buf) - .map_err( - |e| error!("unable to save config to flash: {:?}", e) - ); + .map_err(|e| error!("unable to save channel {} config to flash: {:?}", c, e)); } } } - Command::Ipv4(address) => { - new_ipv4_address = Some(Ipv4Address::from_bytes(&address)); + Command::Ipv4(config) => { + let _ = store + .write_value("ipv4", &config, [0; 16]) + .map_err(|e| error!("unable to save ipv4 config to flash: {:?}", e)); + new_ipv4_config = Some(config); } Command::Reset => { for i in 0..CHANNELS { @@ -401,11 +412,8 @@ fn main() -> ! { } }); - // Apply new IPv4 address - new_ipv4_address.map(|new_ipv4_address| { - server.set_ipv4_address(ipv4_address); - ipv4_address = new_ipv4_address; - }); + // Apply new IPv4 address/gateway + new_ipv4_config.map(|config| server.set_ipv4_config(config)); // Update watchdog wd.feed(); diff --git a/src/net.rs b/src/net.rs index 01f69c6..8e41473 100644 --- a/src/net.rs +++ b/src/net.rs @@ -7,9 +7,13 @@ use stm32f4xx_hal::{ rcc::Clocks, stm32::{interrupt, Peripherals, ETHERNET_MAC, ETHERNET_DMA}, }; -use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface}; +use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv4Cidr}; +use smoltcp::iface::{ + EthernetInterfaceBuilder, EthernetInterface, + NeighborCache, Routes, +}; use stm32_eth::{Eth, RingEntry, PhyAddress, RxDescriptor, TxDescriptor}; +use crate::command_parser::Ipv4Config; use crate::pins::EthernetPins; /// Not on the stack so that stack can be placed in CCMRAM (which the @@ -29,7 +33,7 @@ pub fn run( ethernet_mac: ETHERNET_MAC, ethernet_dma: ETHERNET_DMA, eth_pins: EthernetPins, ethernet_addr: EthernetAddress, - local_addr: Ipv4Address, + ipv4_config: Ipv4Config, f: F ) where F: FnOnce(EthernetInterface<&mut stm32_eth::Eth<'static, 'static>>), @@ -51,15 +55,18 @@ pub fn run( eth_dev.enable_interrupt(); // IP stack - // Netmask 0 means we expect any IP address on the local segment. - // No routing. - let mut ip_addrs = [IpCidr::new(local_addr.into(), 0)]; + let (ipv4_cidr, gateway) = split_ipv4_config(ipv4_config); + let mut ip_addrs = [ipv4_cidr.into()]; let mut neighbor_storage = [None; 16]; let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]); + let mut routes_storage = [None; 1]; + let mut routes = Routes::new(&mut routes_storage[..]); + gateway.map(|gateway| routes.add_default_ipv4_route(gateway).unwrap()); let iface = EthernetInterfaceBuilder::new(&mut eth_dev) .ethernet_addr(ethernet_addr) .ip_addrs(&mut ip_addrs[..]) .neighbor_cache(neighbor_cache) + .routes(routes) .finalize(); f(iface); @@ -90,3 +97,10 @@ pub fn clear_pending(cs: &CriticalSection) { *NET_PENDING.borrow(cs) .borrow_mut() = false; } + +/// utility for destructuring into smoltcp types +pub fn split_ipv4_config(config: Ipv4Config) -> (Ipv4Cidr, Option) { + let cidr = Ipv4Cidr::new(Ipv4Address(config.address), config.mask_len); + let gateway = config.gateway.map(Ipv4Address); + (cidr, gateway) +} diff --git a/src/server.rs b/src/server.rs index d3ac975..7b6da43 100644 --- a/src/server.rs +++ b/src/server.rs @@ -3,9 +3,10 @@ use smoltcp::{ iface::EthernetInterface, socket::{SocketSet, SocketHandle, TcpSocket, TcpSocketBuffer, SocketRef}, time::Instant, - wire::{IpCidr, Ipv4Address, Ipv4Cidr}, + wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}, }; - +use crate::command_parser::Ipv4Config; +use crate::net::split_ipv4_config; pub struct SocketState { handle: SocketHandle, @@ -85,12 +86,12 @@ impl<'a, 'b, S: Default> Server<'a, 'b, S> { } } - pub fn set_ipv4_address(&mut self, ipv4_address: Ipv4Address) { + fn set_ipv4_address(&mut self, ipv4_address: Ipv4Cidr) { self.net.update_ip_addrs(|addrs| { for addr in addrs.iter_mut() { match addr { IpCidr::Ipv4(_) => { - *addr = IpCidr::Ipv4(Ipv4Cidr::new(ipv4_address, 0)); + *addr = IpCidr::Ipv4(ipv4_address); // done break } @@ -101,4 +102,23 @@ impl<'a, 'b, S: Default> Server<'a, 'b, S> { } }); } + + fn set_gateway(&mut self, gateway: Option) { + let routes = self.net.routes_mut(); + match gateway { + None => + routes.update(|routes_storage| { + routes_storage.remove(&IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0)); + }), + Some(gateway) => { + routes.add_default_ipv4_route(gateway).unwrap(); + } + } + } + + pub fn set_ipv4_config(&mut self, config: Ipv4Config) { + let (address, gateway) = split_ipv4_config(config); + self.set_ipv4_address(address); + self.set_gateway(gateway); + } }