Skip to content

Commit

Permalink
DNS server configuration + AP (#87)
Browse files Browse the repository at this point in the history
* Implemented DNS server configuration: Necessary for captive portal + splash page to work.

* Fixed breaking of STA due to something in AP implementation.
Implemented UART for AP commands.

* Fixed acording to review comments.
  • Loading branch information
GustavToft authored Oct 1, 2024
1 parent 06206e0 commit 6666302
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 64 deletions.
53 changes: 41 additions & 12 deletions src/asynch/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize>
Ok(mac.to_be_bytes()[2..].try_into().unwrap())
}

async fn get_wifi_status(&self) -> Result<WifiStatusVal, Error> {
pub async fn get_wifi_status(&self) -> Result<WifiStatusVal, Error> {
match (&self.at_client)
.send_retry(&GetWifiStatus {
status_id: StatusId::Status,
Expand Down Expand Up @@ -333,32 +333,56 @@ impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize>
ap_config_param: AccessPointConfig::IPv4Mode(IPv4Mode::Static),
})
.await?;
}

// Network IP address
if let Some(ip) = options.ip {
(&self.at_client)
.send_retry(&SetWifiAPConfig {
ap_config_id: AccessPointId::Id0,
ap_config_param: AccessPointConfig::IPv4Address(ip),
ap_config_param: AccessPointConfig::IPv4Address(
options.ip.unwrap_or(Ipv4Addr::new(192, 168, 2, 1)),
),
})
.await?;
}
// Network Subnet mask
if let Some(subnet) = options.subnet {

(&self.at_client)
.send_retry(&SetWifiAPConfig {
ap_config_id: AccessPointId::Id0,
ap_config_param: AccessPointConfig::SubnetMask(subnet),
ap_config_param: AccessPointConfig::SubnetMask(
options.subnet.unwrap_or(Ipv4Addr::new(255, 255, 255, 0)),
),
})
.await?;

(&self.at_client)
.send_retry(&SetWifiAPConfig {
ap_config_id: AccessPointId::Id0,
ap_config_param: AccessPointConfig::DefaultGateway(
options.gateway.unwrap_or(Ipv4Addr::new(192, 168, 2, 1)),
),
})
.await?;
}
// Network Default gateway
if let Some(gateway) = options.gateway {

// Network Primary + Secondary DNS
let primary = match options.dns.as_slice() {
&[primary] => Some(primary),
&[primary, secondary] => {
(&self.at_client)
.send_retry(&SetWifiAPConfig {
ap_config_id: AccessPointId::Id0,
ap_config_param: AccessPointConfig::SecondaryDNS(secondary),
})
.await?;

Some(primary)
}
_ => None,
};

Check warning on line 379 in src/asynch/control.rs

View workflow job for this annotation

GitHub Actions / clippy

you don't need to add `&` to all patterns

warning: you don't need to add `&` to all patterns --> src/asynch/control.rs:366:23 | 366 | let primary = match options.dns.as_slice() { | _______________________^ 367 | | &[primary] => Some(primary), 368 | | &[primary, secondary] => { 369 | | (&self.at_client) ... | 378 | | _ => None, 379 | | }; | |_________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats = note: `#[warn(clippy::match_ref_pats)]` on by default help: instead of prefixing all patterns with `&`, you can dereference the expression | 366 ~ let primary = match *options.dns.as_slice() { 367 ~ [primary] => Some(primary), 368 ~ [primary, secondary] => { |

if let Some(primary) = primary {
(&self.at_client)
.send_retry(&SetWifiAPConfig {
ap_config_id: AccessPointId::Id0,
ap_config_param: AccessPointConfig::DefaultGateway(gateway),
ap_config_param: AccessPointConfig::PrimaryDNS(primary),
})
.await?;
}
Expand Down Expand Up @@ -445,11 +469,16 @@ impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize>
})
.await?;

self.state_ch.set_should_connect(true);

Ok(())
}

/// Closes access point.
pub async fn close_ap(&self) -> Result<(), Error> {
self.state_ch.wait_for_initialized().await;
self.state_ch.set_should_connect(false);

(&self.at_client)
.send_retry(&WifiAPAction {
ap_config_id: AccessPointId::Id0,
Expand Down
89 changes: 74 additions & 15 deletions src/asynch/network.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
use core::str::FromStr as _;

use atat::{asynch::AtatClient, UrcChannel, UrcSubscription};
use core::str::FromStr as _;
use embassy_time::{with_timeout, Duration, Timer};
use embedded_hal::digital::OutputPin as _;
use no_std_net::{Ipv4Addr, Ipv6Addr};

use crate::{
command::{
network::{
responses::NetworkStatusResponse,
types::{InterfaceType, NetworkStatus, NetworkStatusParameter},
responses::{APStatusResponse, NetworkStatusResponse},
types::{APStatusParameter, InterfaceType, NetworkStatus, NetworkStatusParameter},
urc::{NetworkDown, NetworkUp},
GetNetworkStatus,
GetAPStatus, GetNetworkStatus,
},
system::{RebootDCE, StoreCurrentConfig},
wifi::{
types::DisconnectReason,
types::{AccessPointStatus, DisconnectReason},
urc::{WifiLinkConnected, WifiLinkDisconnected},
},
Urc,
Expand Down Expand Up @@ -88,12 +87,16 @@ where
connection_id: _,
bssid,
channel,
}) => self.ch.update_connection_with(|con| {
con.wifi_state = WiFiState::Connected;
con.network
.replace(WifiNetwork::new_station(bssid, channel));
}),
}) => {
info!("wifi link connected");
self.ch.update_connection_with(|con| {
con.wifi_state = WiFiState::Connected;
con.network
.replace(WifiNetwork::new_station(bssid, channel));
})
}
Urc::WifiLinkDisconnected(WifiLinkDisconnected { reason, .. }) => {
info!("Wifi link disconnected");
self.ch.update_connection_with(|con| {
con.wifi_state = match reason {
DisconnectReason::NetworkDisabled => {
Expand All @@ -109,19 +112,33 @@ where
}
})
}
Urc::WifiAPUp(_) => warn!("Not yet implemented [WifiAPUp]"),
Urc::WifiAPDown(_) => warn!("Not yet implemented [WifiAPDown]"),
Urc::WifiAPUp(_) => self.ch.update_connection_with(|con| {
con.wifi_state = WiFiState::Connected;
con.network.replace(WifiNetwork::new_ap());
}),
Urc::WifiAPDown(_) => self.ch.update_connection_with(|con| {
con.network.take();
con.wifi_state = WiFiState::Inactive;
}),
Urc::WifiAPStationConnected(_) => warn!("Not yet implemented [WifiAPStationConnected]"),
Urc::WifiAPStationDisconnected(_) => {
warn!("Not yet implemented [WifiAPStationDisconnected]")
}
Urc::EthernetLinkUp(_) => warn!("Not yet implemented [EthernetLinkUp]"),
Urc::EthernetLinkDown(_) => warn!("Not yet implemented [EthernetLinkDown]"),
Urc::NetworkUp(NetworkUp { interface_id }) => {
self.network_status_callback(interface_id).await?;
if interface_id > 10 {
self.ap_status_callback().await?;
} else {
self.network_status_callback(interface_id).await?;
}
}
Urc::NetworkDown(NetworkDown { interface_id }) => {
self.network_status_callback(interface_id).await?;
if interface_id > 10 {
self.ap_status_callback().await?;
} else {
self.network_status_callback(interface_id).await?;
}
}
Urc::NetworkError(_) => warn!("Not yet implemented [NetworkError]"),
_ => {}
Expand All @@ -137,6 +154,7 @@ where
// credentials have been restored from persistent memory. This although
// the wifi station has been started. So we assume that this type is
// also ok.
info!("Entered network_status_callback");
let NetworkStatusResponse {
status:
NetworkStatus::InterfaceType(InterfaceType::WifiStation | InterfaceType::Unknown),
Expand Down Expand Up @@ -165,12 +183,17 @@ where
else {
return Err(Error::Network);
};
info!(
"Network status callback ipv4: {:?}",
core::str::from_utf8(&ipv4).ok()
);

let ipv4_up = core::str::from_utf8(ipv4.as_slice())
.ok()
.and_then(|s| Ipv4Addr::from_str(s).ok())
.map(|ip| !ip.is_unspecified())
.unwrap_or_default();
info!("Network status callback ipv4: {:?}", ipv4_up);

#[cfg(feature = "ipv6")]
let ipv6_up = {
Expand Down Expand Up @@ -208,13 +231,19 @@ where
else {
return Err(Error::Network);
};
info!(
"Network status callback ipv6: {:?}",
core::str::from_utf8(&ipv6_link_local).ok()
);

let ipv6_link_local_up = core::str::from_utf8(ipv6_link_local.as_slice())
.ok()
.and_then(|s| Ipv6Addr::from_str(s).ok())
.map(|ip| !ip.is_unspecified())
.unwrap_or_default();

info!("Network status callback ipv6: {:?}", ipv6_link_local_up);

// Use `ipv4_addr` & `ipv6_addr` to determine link state
self.ch.update_connection_with(|con| {
con.ipv6_link_local_up = ipv6_link_local_up;
Expand All @@ -229,6 +258,36 @@ where
Ok(())
}

async fn ap_status_callback(&mut self) -> Result<(), Error> {
let APStatusResponse {
status_val: AccessPointStatus::Status(ap_status),
..
} = self
.at_client
.send_retry(&GetAPStatus {
status_id: APStatusParameter::Status,
})
.await?
else {
return Err(Error::Network);
};
info!("AP status callback Status: {:?}", ap_status);

let ap_status = ap_status.into();

self.ch.update_connection_with(|con| {
con.ipv6_link_local_up = ap_status;
con.ipv4_up = ap_status;

#[cfg(feature = "ipv6")]
{
con.ipv6_up = ap_status;
}
});

Ok(())
}

async fn wait_startup(&mut self, timeout: Duration) -> Result<(), Error> {
let fut = async {
loop {
Expand Down
10 changes: 10 additions & 0 deletions src/command/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ use types::*;

use super::NoResponse;

/// 7.10 Wi-Fi Acess point status +UWAPSTAT
///
/// Read status of Wi-Fi interface id.
#[derive(Clone, AtatCmd)]
#[at_cmd("+UWAPSTAT", APStatusResponse, attempts = 3, timeout_ms = 1000)]
pub struct GetAPStatus {
#[at_arg(position = 0)]
pub status_id: APStatusParameter,
}

/// 10.1 Network host name +UNHN
///
/// Sets a new host name.
Expand Down
8 changes: 8 additions & 0 deletions src/command/network/responses.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
//! Responses for Network Commands
use crate::command::wifi::types::AccessPointStatus;

use super::types::*;
use atat::atat_derive::AtatResp;

/// 7.10 WiFi AP status +UWAPSTAT
#[derive(Clone, AtatResp)]
pub struct APStatusResponse {
pub status_val: AccessPointStatus,
}

/// 10.2 Network status +UNSTAT
#[derive(Clone, AtatResp)]
pub struct NetworkStatusResponse {
Expand Down
33 changes: 33 additions & 0 deletions src/command/network/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,39 @@ use heapless::String;

use crate::command::OnOff;

#[derive(Clone, PartialEq, AtatEnum)]
pub enum APStatus {
// 0: The <status_val> is the currently used SSID.
#[at_arg(value = 0)]
SSID(String<64>),
// 1: The <status_val> is the currently used BSSID.
#[at_arg(value = 1)]
BSSID(#[at_arg(len = 20)] Bytes<20>),
// 2: The <status_val> is the currently used channel.
#[at_arg(value = 2)]
Channel(u8),
// 3: The <status_val> is the current status of the access point.
// - 0: disabled
// - 1: enabled
#[at_arg(value = 3)]
Status(OnOff),
}

#[derive(Clone, PartialEq, AtatEnum)]
#[repr(u8)]
pub enum APStatusParameter {
// 0: The <status_val> is the currently used SSID.
SSID = 0,
// 1: The <status_val> is the currently used BSSID.
BSSID = 1,
// 2: The <status_val> is the currently used channel.
Channel = 2,
// 3: The <status_val> is the current status of the access point.
// • 0: disabled
// • 1: enabled
Status = 3,
}

#[derive(Clone, PartialEq, AtatEnum)]
pub enum NetworkStatus {
/// 0: The <status_val> is the interface hardware address (displayed only if applicable).
Expand Down
2 changes: 0 additions & 2 deletions src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,13 @@ impl WifiConnection {
}
}

#[allow(dead_code)]
pub fn is_station(&self) -> bool {

Check warning on line 53 in src/connection.rs

View workflow job for this annotation

GitHub Actions / clippy

methods `is_station` and `is_access_point` are never used

warning: methods `is_station` and `is_access_point` are never used --> src/connection.rs:53:12 | 41 | impl WifiConnection { | ------------------- methods in this implementation ... 53 | pub fn is_station(&self) -> bool { | ^^^^^^^^^^ ... 60 | pub fn is_access_point(&self) -> bool { | ^^^^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default

Check warning on line 53 in src/connection.rs

View workflow job for this annotation

GitHub Actions / Test

methods `is_station` and `is_access_point` are never used

Check warning on line 53 in src/connection.rs

View workflow job for this annotation

GitHub Actions / Test

methods `is_station` and `is_access_point` are never used
self.network
.as_ref()
.map(|n| n.mode == WifiMode::Station)
.unwrap_or_default()
}

#[allow(dead_code)]
pub fn is_access_point(&self) -> bool {
!self.is_station()
}
Expand Down
14 changes: 14 additions & 0 deletions src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ impl WifiNetwork {
mode: WifiMode::Station,
}
}

pub fn new_ap() -> Self {
Self {
bssid: Bytes::new(),
op_mode: OperationMode::Infrastructure,
ssid: String::new(),
channel: 0,
rssi: 1,
authentication_suites: 0,
unicast_ciphers: 0,
group_ciphers: 0,
mode: WifiMode::AccessPoint,
}
}
}

impl TryFrom<ScannedWifiNetwork> for WifiNetwork {
Expand Down
Loading

0 comments on commit 6666302

Please sign in to comment.