diff options
| -rw-r--r-- | embassy-net-esp-hosted/src/control.rs | 46 | ||||
| -rw-r--r-- | embassy-net-esp-hosted/src/ioctl.rs | 17 | ||||
| -rw-r--r-- | embassy-net-esp-hosted/src/lib.rs | 5 | ||||
| -rw-r--r-- | embassy-net-esp-hosted/src/proto.rs | 12 |
4 files changed, 73 insertions, 7 deletions
diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 38ec648b4..d96a62daf 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs | |||
| @@ -24,6 +24,11 @@ pub struct Control<'a> { | |||
| 24 | shared: &'a Shared, | 24 | shared: &'a Shared, |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | /// Handle for managing firmware update. | ||
| 28 | pub struct UpdateControl<'a, 'd> { | ||
| 29 | control: &'a mut Control<'d>, | ||
| 30 | } | ||
| 31 | |||
| 27 | /// WiFi mode. | 32 | /// WiFi mode. |
| 28 | #[allow(unused)] | 33 | #[allow(unused)] |
| 29 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | 34 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
| @@ -146,6 +151,13 @@ impl<'a> Control<'a> { | |||
| 146 | Ok(()) | 151 | Ok(()) |
| 147 | } | 152 | } |
| 148 | 153 | ||
| 154 | /// Initiate a firmware update. | ||
| 155 | pub async fn update(&mut self) -> Result<UpdateControl<'_, 'a>, Error> { | ||
| 156 | let req = proto::CtrlMsg_Req_OTABegin {}; | ||
| 157 | ioctl!(self, ReqOtaBegin, RespOtaBegin, req, resp); | ||
| 158 | Ok(UpdateControl { control: self }) | ||
| 159 | } | ||
| 160 | |||
| 149 | /// duration in seconds, clamped to [10, 3600] | 161 | /// duration in seconds, clamped to [10, 3600] |
| 150 | async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> { | 162 | async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> { |
| 151 | let req = proto::CtrlMsg_Req_ConfigHeartbeat { | 163 | let req = proto::CtrlMsg_Req_ConfigHeartbeat { |
| @@ -175,7 +187,8 @@ impl<'a> Control<'a> { | |||
| 175 | async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> { | 187 | async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> { |
| 176 | debug!("ioctl req: {:?}", &msg); | 188 | debug!("ioctl req: {:?}", &msg); |
| 177 | 189 | ||
| 178 | let mut buf = [0u8; 128]; | 190 | // Theoretical max overhead is 29 bytes. Biggest message is OTA write with 256 bytes. |
| 191 | let mut buf = [0u8; 256 + 29]; | ||
| 179 | let buf_len = buf.len(); | 192 | let buf_len = buf.len(); |
| 180 | 193 | ||
| 181 | let mut encoder = PbEncoder::new(&mut buf[..]); | 194 | let mut encoder = PbEncoder::new(&mut buf[..]); |
| @@ -216,6 +229,37 @@ impl<'a> Control<'a> { | |||
| 216 | } | 229 | } |
| 217 | } | 230 | } |
| 218 | 231 | ||
| 232 | impl<'a, 'd> UpdateControl<'a, 'd> { | ||
| 233 | /// Write slice of firmware to a device. | ||
| 234 | /// | ||
| 235 | /// The slice is split into chunks that can be sent across | ||
| 236 | /// the ioctl protocol to the wifi adapter. | ||
| 237 | pub async fn write(&mut self, data: &[u8]) -> Result<(), Error> { | ||
| 238 | let this = &mut self.control; | ||
| 239 | for chunk in data.chunks(256) { | ||
| 240 | let req = proto::CtrlMsg_Req_OTAWrite { | ||
| 241 | ota_data: heapless::Vec::from_slice(chunk).unwrap(), | ||
| 242 | }; | ||
| 243 | ioctl!(this, ReqOtaWrite, RespOtaWrite, req, resp); | ||
| 244 | } | ||
| 245 | Ok(()) | ||
| 246 | } | ||
| 247 | |||
| 248 | /// End the OTA session. | ||
| 249 | /// | ||
| 250 | /// NOTE: Will reset the wifi adapter after 5 seconds. | ||
| 251 | pub async fn finish(self) -> Result<(), Error> { | ||
| 252 | let this = self.control; | ||
| 253 | let req = proto::CtrlMsg_Req_OTAEnd {}; | ||
| 254 | ioctl!(this, ReqOtaEnd, RespOtaEnd, req, resp); | ||
| 255 | // Ensures that run loop awaits reset | ||
| 256 | this.shared.ota_done(); | ||
| 257 | // Wait for re-init | ||
| 258 | this.init().await?; | ||
| 259 | Ok(()) | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 219 | // WHY IS THIS A STRING? WHYYYY | 263 | // WHY IS THIS A STRING? WHYYYY |
| 220 | fn parse_mac(mac: &str) -> Result<[u8; 6], Error> { | 264 | fn parse_mac(mac: &str) -> Result<[u8; 6], Error> { |
| 221 | fn nibble_from_hex(b: u8) -> Result<u8, Error> { | 265 | fn nibble_from_hex(b: u8) -> Result<u8, Error> { |
diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs index a516f80c7..7f462d528 100644 --- a/embassy-net-esp-hosted/src/ioctl.rs +++ b/embassy-net-esp-hosted/src/ioctl.rs | |||
| @@ -24,6 +24,7 @@ pub struct Shared(RefCell<SharedInner>); | |||
| 24 | struct SharedInner { | 24 | struct SharedInner { |
| 25 | ioctl: IoctlState, | 25 | ioctl: IoctlState, |
| 26 | is_init: bool, | 26 | is_init: bool, |
| 27 | is_ota: bool, | ||
| 27 | control_waker: WakerRegistration, | 28 | control_waker: WakerRegistration, |
| 28 | runner_waker: WakerRegistration, | 29 | runner_waker: WakerRegistration, |
| 29 | } | 30 | } |
| @@ -33,6 +34,7 @@ impl Shared { | |||
| 33 | Self(RefCell::new(SharedInner { | 34 | Self(RefCell::new(SharedInner { |
| 34 | ioctl: IoctlState::Done { resp_len: 0 }, | 35 | ioctl: IoctlState::Done { resp_len: 0 }, |
| 35 | is_init: false, | 36 | is_init: false, |
| 37 | is_ota: false, | ||
| 36 | control_waker: WakerRegistration::new(), | 38 | control_waker: WakerRegistration::new(), |
| 37 | runner_waker: WakerRegistration::new(), | 39 | runner_waker: WakerRegistration::new(), |
| 38 | })) | 40 | })) |
| @@ -99,11 +101,26 @@ impl Shared { | |||
| 99 | } | 101 | } |
| 100 | } | 102 | } |
| 101 | 103 | ||
| 104 | // ota | ||
| 105 | pub fn ota_done(&self) { | ||
| 106 | let mut this = self.0.borrow_mut(); | ||
| 107 | this.is_ota = true; | ||
| 108 | this.is_init = false; | ||
| 109 | this.runner_waker.wake(); | ||
| 110 | } | ||
| 111 | |||
| 112 | // check if ota is in progress | ||
| 113 | pub fn is_ota(&self) -> bool { | ||
| 114 | let this = self.0.borrow(); | ||
| 115 | this.is_ota | ||
| 116 | } | ||
| 117 | |||
| 102 | // // // // // // // // // // // // // // // // // // // // | 118 | // // // // // // // // // // // // // // // // // // // // |
| 103 | 119 | ||
| 104 | pub fn init_done(&self) { | 120 | pub fn init_done(&self) { |
| 105 | let mut this = self.0.borrow_mut(); | 121 | let mut this = self.0.borrow_mut(); |
| 106 | this.is_init = true; | 122 | this.is_init = true; |
| 123 | this.is_ota = false; | ||
| 107 | this.control_waker.wake(); | 124 | this.control_waker.wake(); |
| 108 | } | 125 | } |
| 109 | 126 | ||
diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index d882af8cf..7236e73e8 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs | |||
| @@ -234,6 +234,11 @@ where | |||
| 234 | tx_buf[..PayloadHeader::SIZE].fill(0); | 234 | tx_buf[..PayloadHeader::SIZE].fill(0); |
| 235 | } | 235 | } |
| 236 | Either4::Fourth(()) => { | 236 | Either4::Fourth(()) => { |
| 237 | // Extend the deadline if OTA | ||
| 238 | if self.shared.is_ota() { | ||
| 239 | self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP; | ||
| 240 | continue; | ||
| 241 | } | ||
| 237 | panic!("heartbeat from esp32 stopped") | 242 | panic!("heartbeat from esp32 stopped") |
| 238 | } | 243 | } |
| 239 | } | 244 | } |
diff --git a/embassy-net-esp-hosted/src/proto.rs b/embassy-net-esp-hosted/src/proto.rs index 74c67bd61..09bec8984 100644 --- a/embassy-net-esp-hosted/src/proto.rs +++ b/embassy-net-esp-hosted/src/proto.rs | |||
| @@ -16,7 +16,7 @@ Switch to a proper script when https://github.com/YuhanLiin/micropb/issues/30 is | |||
| 16 | // Special config for things that need to be larger | 16 | // Special config for things that need to be larger |
| 17 | g.configure( | 17 | g.configure( |
| 18 | ".CtrlMsg_Req_OTAWrite.ota_data", | 18 | ".CtrlMsg_Req_OTAWrite.ota_data", |
| 19 | micropb_gen::Config::new().max_bytes(1024), | 19 | micropb_gen::Config::new().max_bytes(256), |
| 20 | ); | 20 | ); |
| 21 | g.configure( | 21 | g.configure( |
| 22 | ".CtrlMsg_Event_ESPInit.init_data", | 22 | ".CtrlMsg_Event_ESPInit.init_data", |
| @@ -4296,28 +4296,28 @@ impl ::micropb::MessageEncode for CtrlMsg_Resp_OTABegin { | |||
| 4296 | #[derive(Debug, Default, PartialEq, Clone)] | 4296 | #[derive(Debug, Default, PartialEq, Clone)] |
| 4297 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 4297 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 4298 | pub struct CtrlMsg_Req_OTAWrite { | 4298 | pub struct CtrlMsg_Req_OTAWrite { |
| 4299 | pub r#ota_data: ::micropb::heapless::Vec<u8, 1024>, | 4299 | pub r#ota_data: ::micropb::heapless::Vec<u8, 256>, |
| 4300 | } | 4300 | } |
| 4301 | impl CtrlMsg_Req_OTAWrite { | 4301 | impl CtrlMsg_Req_OTAWrite { |
| 4302 | ///Return a reference to `ota_data` | 4302 | ///Return a reference to `ota_data` |
| 4303 | #[inline] | 4303 | #[inline] |
| 4304 | pub fn r#ota_data(&self) -> &::micropb::heapless::Vec<u8, 1024> { | 4304 | pub fn r#ota_data(&self) -> &::micropb::heapless::Vec<u8, 256> { |
| 4305 | &self.r#ota_data | 4305 | &self.r#ota_data |
| 4306 | } | 4306 | } |
| 4307 | ///Return a mutable reference to `ota_data` | 4307 | ///Return a mutable reference to `ota_data` |
| 4308 | #[inline] | 4308 | #[inline] |
| 4309 | pub fn mut_ota_data(&mut self) -> &mut ::micropb::heapless::Vec<u8, 1024> { | 4309 | pub fn mut_ota_data(&mut self) -> &mut ::micropb::heapless::Vec<u8, 256> { |
| 4310 | &mut self.r#ota_data | 4310 | &mut self.r#ota_data |
| 4311 | } | 4311 | } |
| 4312 | ///Set the value of `ota_data` | 4312 | ///Set the value of `ota_data` |
| 4313 | #[inline] | 4313 | #[inline] |
| 4314 | pub fn set_ota_data(&mut self, value: ::micropb::heapless::Vec<u8, 1024>) -> &mut Self { | 4314 | pub fn set_ota_data(&mut self, value: ::micropb::heapless::Vec<u8, 256>) -> &mut Self { |
| 4315 | self.r#ota_data = value.into(); | 4315 | self.r#ota_data = value.into(); |
| 4316 | self | 4316 | self |
| 4317 | } | 4317 | } |
| 4318 | ///Builder method that sets the value of `ota_data`. Useful for initializing the message. | 4318 | ///Builder method that sets the value of `ota_data`. Useful for initializing the message. |
| 4319 | #[inline] | 4319 | #[inline] |
| 4320 | pub fn init_ota_data(mut self, value: ::micropb::heapless::Vec<u8, 1024>) -> Self { | 4320 | pub fn init_ota_data(mut self, value: ::micropb::heapless::Vec<u8, 256>) -> Self { |
| 4321 | self.r#ota_data = value.into(); | 4321 | self.r#ota_data = value.into(); |
| 4322 | self | 4322 | self |
| 4323 | } | 4323 | } |
