diff options
Diffstat (limited to 'embassy-usb-dfu/src/dfu.rs')
| -rw-r--r-- | embassy-usb-dfu/src/dfu.rs | 78 |
1 files changed, 49 insertions, 29 deletions
diff --git a/embassy-usb-dfu/src/dfu.rs b/embassy-usb-dfu/src/dfu.rs index e99aa70c3..abd929a9e 100644 --- a/embassy-usb-dfu/src/dfu.rs +++ b/embassy-usb-dfu/src/dfu.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | 2 | ||
| 3 | use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater}; | 3 | use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError}; |
| 4 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; | 4 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; |
| 5 | use embassy_usb::driver::Driver; | 5 | use embassy_usb::driver::Driver; |
| 6 | use embassy_usb::{Builder, Handler}; | 6 | use embassy_usb::{Builder, Handler}; |
| @@ -19,6 +19,7 @@ pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_S | |||
| 19 | state: State, | 19 | state: State, |
| 20 | status: Status, | 20 | status: Status, |
| 21 | offset: usize, | 21 | offset: usize, |
| 22 | buf: AlignedBuffer<BLOCK_SIZE>, | ||
| 22 | _rst: PhantomData<RST>, | 23 | _rst: PhantomData<RST>, |
| 23 | } | 24 | } |
| 24 | 25 | ||
| @@ -31,6 +32,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Co | |||
| 31 | state: State::DfuIdle, | 32 | state: State::DfuIdle, |
| 32 | status: Status::Ok, | 33 | status: Status::Ok, |
| 33 | offset: 0, | 34 | offset: 0, |
| 35 | buf: AlignedBuffer([0; BLOCK_SIZE]), | ||
| 34 | _rst: PhantomData, | 36 | _rst: PhantomData, |
| 35 | } | 37 | } |
| 36 | } | 38 | } |
| @@ -42,6 +44,20 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Co | |||
| 42 | } | 44 | } |
| 43 | } | 45 | } |
| 44 | 46 | ||
| 47 | impl From<FirmwareUpdaterError> for Status { | ||
| 48 | fn from(e: FirmwareUpdaterError) -> Self { | ||
| 49 | match e { | ||
| 50 | FirmwareUpdaterError::Flash(e) => match e { | ||
| 51 | NorFlashErrorKind::NotAligned => Status::ErrWrite, | ||
| 52 | NorFlashErrorKind::OutOfBounds => Status::ErrAddress, | ||
| 53 | _ => Status::ErrUnknown, | ||
| 54 | }, | ||
| 55 | FirmwareUpdaterError::Signature(_) => Status::ErrVerify, | ||
| 56 | FirmwareUpdaterError::BadState => Status::ErrUnknown, | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 45 | impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Handler | 61 | impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Handler |
| 46 | for Control<'d, DFU, STATE, RST, BLOCK_SIZE> | 62 | for Control<'d, DFU, STATE, RST, BLOCK_SIZE> |
| 47 | { | 63 | { |
| @@ -51,65 +67,67 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha | |||
| 51 | data: &[u8], | 67 | data: &[u8], |
| 52 | ) -> Option<embassy_usb::control::OutResponse> { | 68 | ) -> Option<embassy_usb::control::OutResponse> { |
| 53 | if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) { | 69 | if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) { |
| 70 | debug!("Unknown out request: {:?}", req); | ||
| 54 | return None; | 71 | return None; |
| 55 | } | 72 | } |
| 56 | match Request::try_from(req.request) { | 73 | match Request::try_from(req.request) { |
| 57 | Ok(Request::Abort) => { | 74 | Ok(Request::Abort) => { |
| 75 | info!("Abort requested"); | ||
| 58 | self.reset_state(); | 76 | self.reset_state(); |
| 59 | Some(OutResponse::Accepted) | 77 | Some(OutResponse::Accepted) |
| 60 | } | 78 | } |
| 61 | Ok(Request::Dnload) if self.attrs.contains(DfuAttributes::CAN_DOWNLOAD) => { | 79 | Ok(Request::Dnload) if self.attrs.contains(DfuAttributes::CAN_DOWNLOAD) => { |
| 62 | if req.value == 0 { | 80 | if req.value == 0 { |
| 81 | info!("Download starting"); | ||
| 63 | self.state = State::Download; | 82 | self.state = State::Download; |
| 64 | self.offset = 0; | 83 | self.offset = 0; |
| 65 | } | 84 | } |
| 66 | 85 | ||
| 67 | let mut buf = AlignedBuffer([0; BLOCK_SIZE]); | 86 | if self.state != State::Download { |
| 68 | buf.as_mut()[..data.len()].copy_from_slice(data); | 87 | error!("Unexpected DNLOAD while chip is waiting for a GETSTATUS"); |
| 88 | self.status = Status::ErrUnknown; | ||
| 89 | self.state = State::Error; | ||
| 90 | return Some(OutResponse::Rejected); | ||
| 91 | } | ||
| 92 | |||
| 93 | if data.len() > BLOCK_SIZE { | ||
| 94 | error!("USB data len exceeded block size"); | ||
| 95 | self.status = Status::ErrUnknown; | ||
| 96 | self.state = State::Error; | ||
| 97 | return Some(OutResponse::Rejected); | ||
| 98 | } | ||
| 99 | |||
| 100 | debug!("Copying {} bytes to buffer", data.len()); | ||
| 101 | self.buf.as_mut()[..data.len()].copy_from_slice(data); | ||
| 102 | |||
| 103 | let final_transfer = req.length == 0; | ||
| 104 | if final_transfer { | ||
| 105 | debug!("Receiving final transfer"); | ||
| 69 | 106 | ||
| 70 | if req.length == 0 { | ||
| 71 | match self.updater.mark_updated() { | 107 | match self.updater.mark_updated() { |
| 72 | Ok(_) => { | 108 | Ok(_) => { |
| 73 | self.status = Status::Ok; | 109 | self.status = Status::Ok; |
| 74 | self.state = State::ManifestSync; | 110 | self.state = State::ManifestSync; |
| 111 | info!("Update complete"); | ||
| 75 | } | 112 | } |
| 76 | Err(e) => { | 113 | Err(e) => { |
| 114 | error!("Error completing update: {}", e); | ||
| 77 | self.state = State::Error; | 115 | self.state = State::Error; |
| 78 | match e { | 116 | self.status = e.into(); |
| 79 | embassy_boot::FirmwareUpdaterError::Flash(e) => match e { | ||
| 80 | NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite, | ||
| 81 | NorFlashErrorKind::OutOfBounds => self.status = Status::ErrAddress, | ||
| 82 | _ => self.status = Status::ErrUnknown, | ||
| 83 | }, | ||
| 84 | embassy_boot::FirmwareUpdaterError::Signature(_) => self.status = Status::ErrVerify, | ||
| 85 | embassy_boot::FirmwareUpdaterError::BadState => self.status = Status::ErrUnknown, | ||
| 86 | } | ||
| 87 | } | 117 | } |
| 88 | } | 118 | } |
| 89 | } else { | 119 | } else { |
| 90 | if self.state != State::Download { | 120 | debug!("Writing {} bytes at {}", data.len(), self.offset); |
| 91 | // Unexpected DNLOAD while chip is waiting for a GETSTATUS | 121 | match self.updater.write_firmware(self.offset, self.buf.as_ref()) { |
| 92 | self.status = Status::ErrUnknown; | ||
| 93 | self.state = State::Error; | ||
| 94 | return Some(OutResponse::Rejected); | ||
| 95 | } | ||
| 96 | match self.updater.write_firmware(self.offset, buf.as_ref()) { | ||
| 97 | Ok(_) => { | 122 | Ok(_) => { |
| 98 | self.status = Status::Ok; | 123 | self.status = Status::Ok; |
| 99 | self.state = State::DlSync; | 124 | self.state = State::DlSync; |
| 100 | self.offset += data.len(); | 125 | self.offset += data.len(); |
| 101 | } | 126 | } |
| 102 | Err(e) => { | 127 | Err(e) => { |
| 128 | error!("Error writing firmware: {:?}", e); | ||
| 103 | self.state = State::Error; | 129 | self.state = State::Error; |
| 104 | match e { | 130 | self.status = e.into(); |
| 105 | embassy_boot::FirmwareUpdaterError::Flash(e) => match e { | ||
| 106 | NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite, | ||
| 107 | NorFlashErrorKind::OutOfBounds => self.status = Status::ErrAddress, | ||
| 108 | _ => self.status = Status::ErrUnknown, | ||
| 109 | }, | ||
| 110 | embassy_boot::FirmwareUpdaterError::Signature(_) => self.status = Status::ErrVerify, | ||
| 111 | embassy_boot::FirmwareUpdaterError::BadState => self.status = Status::ErrUnknown, | ||
| 112 | } | ||
| 113 | } | 131 | } |
| 114 | } | 132 | } |
| 115 | } | 133 | } |
| @@ -118,6 +136,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha | |||
| 118 | } | 136 | } |
| 119 | Ok(Request::Detach) => Some(OutResponse::Accepted), // Device is already in DFU mode | 137 | Ok(Request::Detach) => Some(OutResponse::Accepted), // Device is already in DFU mode |
| 120 | Ok(Request::ClrStatus) => { | 138 | Ok(Request::ClrStatus) => { |
| 139 | info!("Clear status requested"); | ||
| 121 | self.reset_state(); | 140 | self.reset_state(); |
| 122 | Some(OutResponse::Accepted) | 141 | Some(OutResponse::Accepted) |
| 123 | } | 142 | } |
| @@ -131,6 +150,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha | |||
| 131 | buf: &'a mut [u8], | 150 | buf: &'a mut [u8], |
| 132 | ) -> Option<embassy_usb::control::InResponse<'a>> { | 151 | ) -> Option<embassy_usb::control::InResponse<'a>> { |
| 133 | if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) { | 152 | if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) { |
| 153 | debug!("Unknown in request: {:?}", req); | ||
| 134 | return None; | 154 | return None; |
| 135 | } | 155 | } |
| 136 | match Request::try_from(req.request) { | 156 | match Request::try_from(req.request) { |
