embassy/embassy-net-esp-hosted/src/control.rs

170 lines
5 KiB
Rust
Raw Normal View History

2023-06-09 01:36:48 +00:00
use ch::driver::LinkState;
use embassy_net_driver_channel as ch;
use heapless::String;
use crate::ioctl::Shared;
2023-06-09 01:36:48 +00:00
use crate::proto::{self, CtrlMsg};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
Failed(u32),
Timeout,
Internal,
2023-06-09 01:36:48 +00:00
}
pub struct Control<'a> {
state_ch: ch::StateRunner<'a>,
shared: &'a Shared,
2023-06-09 01:36:48 +00:00
}
2023-06-21 17:08:09 +00:00
#[allow(unused)]
2023-06-09 01:36:48 +00:00
enum WifiMode {
None = 0,
Sta = 1,
Ap = 2,
ApSta = 3,
}
macro_rules! ioctl {
($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => {
let mut msg = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::$req_variant as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::$req_variant($req)),
};
$self.ioctl(&mut msg).await?;
let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else {
warn!("unexpected response variant");
return Err(Error::Internal);
};
if $resp.resp != 0 {
return Err(Error::Failed($resp.resp));
}
};
}
2023-06-09 01:36:48 +00:00
impl<'a> Control<'a> {
pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self {
Self { state_ch, shared }
2023-06-09 01:36:48 +00:00
}
pub async fn init(&mut self) -> Result<(), Error> {
debug!("wait for init event...");
self.shared.init_wait().await;
debug!("set heartbeat");
self.set_heartbeat(10).await?;
2023-06-09 01:36:48 +00:00
debug!("set wifi mode");
self.set_wifi_mode(WifiMode::Sta as _).await?;
2023-06-21 17:08:09 +00:00
let mac_addr = self.get_mac_addr().await?;
2023-06-09 01:36:48 +00:00
debug!("mac addr: {:02x}", mac_addr);
self.state_ch.set_ethernet_address(mac_addr);
Ok(())
2023-06-09 01:36:48 +00:00
}
pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
let req = proto::CtrlMsgReqConnectAp {
ssid: String::from(ssid),
pwd: String::from(password),
bssid: String::new(),
listen_interval: 3,
is_wpa3_supported: false,
2023-06-09 01:36:48 +00:00
};
ioctl!(self, ReqConnectAp, RespConnectAp, req, resp);
2023-06-09 01:36:48 +00:00
self.state_ch.set_link_state(LinkState::Up);
Ok(())
2023-06-09 01:36:48 +00:00
}
pub async fn disconnect(&mut self) -> Result<(), Error> {
let req = proto::CtrlMsgReqGetStatus {};
ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp);
self.state_ch.set_link_state(LinkState::Up);
Ok(())
}
/// duration in seconds, clamped to [10, 3600]
async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> {
let req = proto::CtrlMsgReqConfigHeartbeat { enable: true, duration };
ioctl!(self, ReqConfigHeartbeat, RespConfigHeartbeat, req, resp);
Ok(())
}
async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> {
let req = proto::CtrlMsgReqGetMacAddress {
mode: WifiMode::Sta as _,
2023-07-04 19:34:55 +00:00
};
ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp);
2023-06-09 01:36:48 +00:00
// WHY IS THIS A STRING? WHYYYY
fn nibble_from_hex(b: u8) -> u8 {
match b {
b'0'..=b'9' => b - b'0',
b'a'..=b'f' => b + 0xa - b'a',
b'A'..=b'F' => b + 0xa - b'A',
_ => panic!("invalid hex digit {}", b),
}
}
let mac = resp.mac.as_bytes();
let mut res = [0; 6];
if mac.len() != 17 {
warn!("unexpected MAC respnse length");
return Err(Error::Internal);
}
2023-06-09 01:36:48 +00:00
for (i, b) in res.iter_mut().enumerate() {
*b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1])
}
Ok(res)
2023-06-09 01:36:48 +00:00
}
async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> {
let req = proto::CtrlMsgReqSetMode { mode };
ioctl!(self, ReqSetWifiMode, RespSetWifiMode, req, resp);
Ok(())
2023-06-09 01:36:48 +00:00
}
async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> {
debug!("ioctl req: {:?}", &msg);
2023-06-21 16:13:19 +00:00
2023-06-09 01:36:48 +00:00
let mut buf = [0u8; 128];
let req_len = noproto::write(msg, &mut buf).map_err(|_| {
warn!("failed to serialize control request");
Error::Internal
})?;
2023-06-09 01:36:48 +00:00
struct CancelOnDrop<'a>(&'a Shared);
2023-06-09 01:36:48 +00:00
impl CancelOnDrop<'_> {
fn defuse(self) {
core::mem::forget(self);
}
}
impl Drop for CancelOnDrop<'_> {
fn drop(&mut self) {
self.0.ioctl_cancel();
2023-06-09 01:36:48 +00:00
}
}
let ioctl = CancelOnDrop(self.shared);
2023-06-09 01:36:48 +00:00
let resp_len = ioctl.0.ioctl(&mut buf, req_len).await;
2023-06-09 01:36:48 +00:00
ioctl.defuse();
*msg = noproto::read(&buf[..resp_len]).map_err(|_| {
warn!("failed to serialize control request");
Error::Internal
})?;
debug!("ioctl resp: {:?}", msg);
2023-06-21 16:13:19 +00:00
Ok(())
2023-06-09 01:36:48 +00:00
}
}