use ch::driver::LinkState; use defmt::Debug2Format; use embassy_net_driver_channel as ch; use heapless::String; use crate::ioctl::Shared; use crate::proto::{self, CtrlMsg}; #[derive(Debug)] pub struct Error { pub status: u32, } pub struct Control<'a> { state_ch: ch::StateRunner<'a>, shared: &'a Shared, } #[allow(unused)] enum WifiMode { None = 0, Sta = 1, Ap = 2, ApSta = 3, } impl<'a> Control<'a> { pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self { Self { state_ch, shared } } pub async fn init(&mut self) { debug!("wait for init event..."); self.shared.init_wait().await; debug!("set wifi mode"); self.set_wifi_mode(WifiMode::Sta as _).await; let mac_addr = self.get_mac_addr().await; debug!("mac addr: {:02x}", mac_addr); self.state_ch.set_ethernet_address(mac_addr); } pub async fn join(&mut self, ssid: &str, password: &str) { let req = proto::CtrlMsg { msg_id: proto::CtrlMsgId::ReqConnectAp as _, msg_type: proto::CtrlMsgType::Req as _, payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp { ssid: String::from(ssid), pwd: String::from(password), bssid: String::new(), listen_interval: 3, is_wpa3_supported: false, })), }; let resp = self.ioctl(req).await; let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; debug!("======= {:?}", Debug2Format(&resp)); assert_eq!(resp.resp, 0); self.state_ch.set_link_state(LinkState::Up); } async fn get_mac_addr(&mut self) -> [u8; 6] { let req = proto::CtrlMsg { msg_id: proto::CtrlMsgId::ReqGetMacAddress as _, msg_type: proto::CtrlMsgType::Req as _, payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress( proto::CtrlMsgReqGetMacAddress { mode: WifiMode::Sta as _, }, )), }; let resp = self.ioctl(req).await; let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; assert_eq!(resp.resp, 0); // 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]; assert_eq!(mac.len(), 17); for (i, b) in res.iter_mut().enumerate() { *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1]) } res } async fn set_wifi_mode(&mut self, mode: u32) { let req = proto::CtrlMsg { msg_id: proto::CtrlMsgId::ReqSetWifiMode as _, msg_type: proto::CtrlMsgType::Req as _, payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })), }; let resp = self.ioctl(req).await; let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; assert_eq!(resp.resp, 0); } async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg { debug!("ioctl req: {:?}", &req); let mut buf = [0u8; 128]; let req_len = noproto::write(&req, &mut buf).unwrap(); struct CancelOnDrop<'a>(&'a Shared); impl CancelOnDrop<'_> { fn defuse(self) { core::mem::forget(self); } } impl Drop for CancelOnDrop<'_> { fn drop(&mut self) { self.0.ioctl_cancel(); } } let ioctl = CancelOnDrop(self.shared); let resp_len = ioctl.0.ioctl(&mut buf, req_len).await; ioctl.defuse(); let res = noproto::read(&buf[..resp_len]).unwrap(); debug!("ioctl resp: {:?}", &res); res } }