Merge branch 'master' into flash-regions
This commit is contained in:
		
						commit
						3deb65bc87
					
				@ -6,7 +6,7 @@ version = "0.1.0"
 | 
				
			|||||||
license = "MIT OR Apache-2.0"
 | 
					license = "MIT OR Apache-2.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
 | 
				
			||||||
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
 | 
					embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
cortex-m = "0.7"
 | 
					cortex-m = "0.7"
 | 
				
			||||||
cortex-m-rt = "0.7"
 | 
					cortex-m-rt = "0.7"
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false  }
 | 
					embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false  }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly"] }
 | 
					embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defmt = "0.3.0"
 | 
					defmt = "0.3.0"
 | 
				
			||||||
defmt-rtt = "0.3.0"
 | 
					defmt-rtt = "0.3.0"
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,7 @@ where
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Extension of the embedded-storage flash type information with block size and erase value.
 | 
					/// Extension of the embedded-storage flash type information with block size and erase value.
 | 
				
			||||||
pub trait Flash: NorFlash + ReadNorFlash {
 | 
					pub trait Flash: NorFlash {
 | 
				
			||||||
    /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase
 | 
					    /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase
 | 
				
			||||||
    /// size of the flash, but for external QSPI flash modules, this can be lower.
 | 
					    /// size of the flash, but for external QSPI flash modules, this can be lower.
 | 
				
			||||||
    const BLOCK_SIZE: usize;
 | 
					    const BLOCK_SIZE: usize;
 | 
				
			||||||
@ -60,9 +60,11 @@ pub trait FlashConfig {
 | 
				
			|||||||
/// different page sizes and flash write sizes.
 | 
					/// different page sizes and flash write sizes.
 | 
				
			||||||
pub struct BootLoader {
 | 
					pub struct BootLoader {
 | 
				
			||||||
    // Page with current state of bootloader. The state partition has the following format:
 | 
					    // Page with current state of bootloader. The state partition has the following format:
 | 
				
			||||||
    // | Range          | Description                                                                      |
 | 
					    // All ranges are in multiples of WRITE_SIZE bytes.
 | 
				
			||||||
    // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
 | 
					    // | Range    | Description                                                                      |
 | 
				
			||||||
    // | WRITE_SIZE - N | Progress index used while swapping or reverting                                  |
 | 
					    // | 0..1     | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
 | 
				
			||||||
 | 
					    // | 1..2     | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid.          |
 | 
				
			||||||
 | 
					    // | 2..2 + N | Progress index used while swapping or reverting                                  |
 | 
				
			||||||
    state: Partition,
 | 
					    state: Partition,
 | 
				
			||||||
    // Location of the partition which will be booted from
 | 
					    // Location of the partition which will be booted from
 | 
				
			||||||
    active: Partition,
 | 
					    active: Partition,
 | 
				
			||||||
@ -79,7 +81,7 @@ impl BootLoader {
 | 
				
			|||||||
        Self { active, dfu, state }
 | 
					        Self { active, dfu, state }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Return the boot address for the active partition.
 | 
					    /// Return the offset of the active partition into the active flash.
 | 
				
			||||||
    pub fn boot_address(&self) -> usize {
 | 
					    pub fn boot_address(&self) -> usize {
 | 
				
			||||||
        self.active.from
 | 
					        self.active.from
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -192,14 +194,19 @@ impl BootLoader {
 | 
				
			|||||||
                trace!("Reverting");
 | 
					                trace!("Reverting");
 | 
				
			||||||
                self.revert(p, magic, page)?;
 | 
					                self.revert(p, magic, page)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Overwrite magic and reset progress
 | 
					                let state_flash = p.state();
 | 
				
			||||||
                let fstate = p.state();
 | 
					 | 
				
			||||||
                magic.fill(!P::STATE::ERASE_VALUE);
 | 
					 | 
				
			||||||
                fstate.write(self.state.from as u32, magic)?;
 | 
					 | 
				
			||||||
                fstate.erase(self.state.from as u32, self.state.to as u32)?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Invalidate progress
 | 
				
			||||||
 | 
					                magic.fill(!P::STATE::ERASE_VALUE);
 | 
				
			||||||
 | 
					                self.state
 | 
				
			||||||
 | 
					                    .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, magic)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Clear magic and progress
 | 
				
			||||||
 | 
					                self.state.wipe_blocking(state_flash)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Set magic
 | 
				
			||||||
                magic.fill(BOOT_MAGIC);
 | 
					                magic.fill(BOOT_MAGIC);
 | 
				
			||||||
                fstate.write(self.state.from as u32, magic)?;
 | 
					                self.state.write_blocking(state_flash, 0, magic)?;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(state)
 | 
					        Ok(state)
 | 
				
			||||||
@ -215,62 +222,61 @@ impl BootLoader {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> {
 | 
					    fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> {
 | 
				
			||||||
        let write_size = aligned.len();
 | 
					        let write_size = aligned.len();
 | 
				
			||||||
        let max_index = ((self.state.len() - write_size) / write_size) - 1;
 | 
					        let max_index = ((self.state.len() - write_size) / write_size) - 2;
 | 
				
			||||||
        aligned.fill(!P::STATE::ERASE_VALUE);
 | 
					        aligned.fill(!P::STATE::ERASE_VALUE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let flash = config.state();
 | 
					        let state_flash = config.state();
 | 
				
			||||||
        for i in 0..max_index {
 | 
					
 | 
				
			||||||
            flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?;
 | 
					        self.state
 | 
				
			||||||
 | 
					            .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, aligned)?;
 | 
				
			||||||
 | 
					        if aligned.iter().any(|&b| b != P::STATE::ERASE_VALUE) {
 | 
				
			||||||
 | 
					            // Progress is invalid
 | 
				
			||||||
 | 
					            return Ok(max_index);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for index in 0..max_index {
 | 
				
			||||||
 | 
					            self.state
 | 
				
			||||||
 | 
					                .read_blocking(state_flash, (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) {
 | 
					            if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) {
 | 
				
			||||||
                return Ok(i);
 | 
					                return Ok(index);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(max_index)
 | 
					        Ok(max_index)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> {
 | 
					    fn update_progress<P: FlashConfig>(&mut self, index: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> {
 | 
				
			||||||
        let flash = p.state();
 | 
					 | 
				
			||||||
        let write_size = magic.len();
 | 
					 | 
				
			||||||
        let w = self.state.from + write_size + idx * write_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let aligned = magic;
 | 
					        let aligned = magic;
 | 
				
			||||||
        aligned.fill(!P::STATE::ERASE_VALUE);
 | 
					        aligned.fill(!P::STATE::ERASE_VALUE);
 | 
				
			||||||
        flash.write(w as u32, aligned)?;
 | 
					        self.state
 | 
				
			||||||
 | 
					            .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?;
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn active_addr(&self, n: usize, page_size: usize) -> usize {
 | 
					 | 
				
			||||||
        self.active.from + n * page_size
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn dfu_addr(&self, n: usize, page_size: usize) -> usize {
 | 
					 | 
				
			||||||
        self.dfu.from + n * page_size
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn copy_page_once_to_active<P: FlashConfig>(
 | 
					    fn copy_page_once_to_active<P: FlashConfig>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        idx: usize,
 | 
					        idx: usize,
 | 
				
			||||||
        from_page: usize,
 | 
					        from_offset: u32,
 | 
				
			||||||
        to_page: usize,
 | 
					        to_offset: u32,
 | 
				
			||||||
        p: &mut P,
 | 
					        p: &mut P,
 | 
				
			||||||
        magic: &mut [u8],
 | 
					        magic: &mut [u8],
 | 
				
			||||||
        page: &mut [u8],
 | 
					        page: &mut [u8],
 | 
				
			||||||
    ) -> Result<(), BootError> {
 | 
					    ) -> Result<(), BootError> {
 | 
				
			||||||
        let buf = page;
 | 
					        let buf = page;
 | 
				
			||||||
        if self.current_progress(p, magic)? <= idx {
 | 
					        if self.current_progress(p, magic)? <= idx {
 | 
				
			||||||
            let mut offset = from_page;
 | 
					            let mut offset = from_offset;
 | 
				
			||||||
            for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) {
 | 
					            for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) {
 | 
				
			||||||
                p.dfu().read(offset as u32, chunk)?;
 | 
					                self.dfu.read_blocking(p.dfu(), offset, chunk)?;
 | 
				
			||||||
                offset += chunk.len();
 | 
					                offset += chunk.len() as u32;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?;
 | 
					            self.active
 | 
				
			||||||
 | 
					                .erase_blocking(p.active(), to_offset, to_offset + buf.len() as u32)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let mut offset = to_page;
 | 
					            let mut offset = to_offset;
 | 
				
			||||||
            for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) {
 | 
					            for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) {
 | 
				
			||||||
                p.active().write(offset as u32, chunk)?;
 | 
					                self.active.write_blocking(p.active(), offset, chunk)?;
 | 
				
			||||||
                offset += chunk.len();
 | 
					                offset += chunk.len() as u32;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            self.update_progress(idx, p, magic)?;
 | 
					            self.update_progress(idx, p, magic)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -280,26 +286,27 @@ impl BootLoader {
 | 
				
			|||||||
    fn copy_page_once_to_dfu<P: FlashConfig>(
 | 
					    fn copy_page_once_to_dfu<P: FlashConfig>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        idx: usize,
 | 
					        idx: usize,
 | 
				
			||||||
        from_page: usize,
 | 
					        from_offset: u32,
 | 
				
			||||||
        to_page: usize,
 | 
					        to_offset: u32,
 | 
				
			||||||
        p: &mut P,
 | 
					        p: &mut P,
 | 
				
			||||||
        magic: &mut [u8],
 | 
					        magic: &mut [u8],
 | 
				
			||||||
        page: &mut [u8],
 | 
					        page: &mut [u8],
 | 
				
			||||||
    ) -> Result<(), BootError> {
 | 
					    ) -> Result<(), BootError> {
 | 
				
			||||||
        let buf = page;
 | 
					        let buf = page;
 | 
				
			||||||
        if self.current_progress(p, magic)? <= idx {
 | 
					        if self.current_progress(p, magic)? <= idx {
 | 
				
			||||||
            let mut offset = from_page;
 | 
					            let mut offset = from_offset;
 | 
				
			||||||
            for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) {
 | 
					            for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) {
 | 
				
			||||||
                p.active().read(offset as u32, chunk)?;
 | 
					                self.active.read_blocking(p.active(), offset, chunk)?;
 | 
				
			||||||
                offset += chunk.len();
 | 
					                offset += chunk.len() as u32;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?;
 | 
					            self.dfu
 | 
				
			||||||
 | 
					                .erase_blocking(p.dfu(), to_offset as u32, to_offset + buf.len() as u32)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let mut offset = to_page;
 | 
					            let mut offset = to_offset;
 | 
				
			||||||
            for chunk in buf.chunks(P::DFU::BLOCK_SIZE) {
 | 
					            for chunk in buf.chunks(P::DFU::BLOCK_SIZE) {
 | 
				
			||||||
                p.dfu().write(offset as u32, chunk)?;
 | 
					                self.dfu.write_blocking(p.dfu(), offset, chunk)?;
 | 
				
			||||||
                offset += chunk.len();
 | 
					                offset += chunk.len() as u32;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            self.update_progress(idx, p, magic)?;
 | 
					            self.update_progress(idx, p, magic)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -312,17 +319,20 @@ impl BootLoader {
 | 
				
			|||||||
        trace!("Page count: {}", page_count);
 | 
					        trace!("Page count: {}", page_count);
 | 
				
			||||||
        for page_num in 0..page_count {
 | 
					        for page_num in 0..page_count {
 | 
				
			||||||
            trace!("COPY PAGE {}", page_num);
 | 
					            trace!("COPY PAGE {}", page_num);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let idx = page_num * 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Copy active page to the 'next' DFU page.
 | 
					            // Copy active page to the 'next' DFU page.
 | 
				
			||||||
            let active_page = self.active_addr(page_count - 1 - page_num, page_size);
 | 
					            let active_from_offset = ((page_count - 1 - page_num) * page_size) as u32;
 | 
				
			||||||
            let dfu_page = self.dfu_addr(page_count - page_num, page_size);
 | 
					            let dfu_to_offset = ((page_count - page_num) * page_size) as u32;
 | 
				
			||||||
            //trace!("Copy active {} to dfu {}", active_page, dfu_page);
 | 
					            //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset);
 | 
				
			||||||
            self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?;
 | 
					            self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Copy DFU page to the active page
 | 
					            // Copy DFU page to the active page
 | 
				
			||||||
            let active_page = self.active_addr(page_count - 1 - page_num, page_size);
 | 
					            let active_to_offset = ((page_count - 1 - page_num) * page_size) as u32;
 | 
				
			||||||
            let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size);
 | 
					            let dfu_from_offset = ((page_count - 1 - page_num) * page_size) as u32;
 | 
				
			||||||
            //trace!("Copy dfy {} to active {}", dfu_page, active_page);
 | 
					            //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset);
 | 
				
			||||||
            self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?;
 | 
					            self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
@ -332,23 +342,24 @@ impl BootLoader {
 | 
				
			|||||||
        let page_size = page.len();
 | 
					        let page_size = page.len();
 | 
				
			||||||
        let page_count = self.active.len() / page_size;
 | 
					        let page_count = self.active.len() / page_size;
 | 
				
			||||||
        for page_num in 0..page_count {
 | 
					        for page_num in 0..page_count {
 | 
				
			||||||
 | 
					            let idx = page_count * 2 + page_num * 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Copy the bad active page to the DFU page
 | 
					            // Copy the bad active page to the DFU page
 | 
				
			||||||
            let active_page = self.active_addr(page_num, page_size);
 | 
					            let active_from_offset = (page_num * page_size) as u32;
 | 
				
			||||||
            let dfu_page = self.dfu_addr(page_num, page_size);
 | 
					            let dfu_to_offset = (page_num * page_size) as u32;
 | 
				
			||||||
            self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?;
 | 
					            self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Copy the DFU page back to the active page
 | 
					            // Copy the DFU page back to the active page
 | 
				
			||||||
            let active_page = self.active_addr(page_num, page_size);
 | 
					            let active_to_offset = (page_num * page_size) as u32;
 | 
				
			||||||
            let dfu_page = self.dfu_addr(page_num + 1, page_size);
 | 
					            let dfu_from_offset = ((page_num + 1) * page_size) as u32;
 | 
				
			||||||
            self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?;
 | 
					            self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn read_state<P: FlashConfig>(&mut self, config: &mut P, magic: &mut [u8]) -> Result<State, BootError> {
 | 
					    fn read_state<P: FlashConfig>(&mut self, config: &mut P, magic: &mut [u8]) -> Result<State, BootError> {
 | 
				
			||||||
        let flash = config.state();
 | 
					        self.state.read_blocking(config.state(), 0, magic)?;
 | 
				
			||||||
        flash.read(self.state.from as u32, magic)?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if !magic.iter().any(|&b| b != SWAP_MAGIC) {
 | 
					        if !magic.iter().any(|&b| b != SWAP_MAGIC) {
 | 
				
			||||||
            Ok(State::Swap)
 | 
					            Ok(State::Swap)
 | 
				
			||||||
@ -362,7 +373,7 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s
 | 
				
			|||||||
    assert_eq!(active.len() % page_size, 0);
 | 
					    assert_eq!(active.len() % page_size, 0);
 | 
				
			||||||
    assert_eq!(dfu.len() % page_size, 0);
 | 
					    assert_eq!(dfu.len() % page_size, 0);
 | 
				
			||||||
    assert!(dfu.len() - active.len() >= page_size);
 | 
					    assert!(dfu.len() - active.len() >= page_size);
 | 
				
			||||||
    assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size);
 | 
					    assert!(2 + 2 * (active.len() / page_size) <= state.len() / write_size);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A flash wrapper implementing the Flash and embedded_storage traits.
 | 
					/// A flash wrapper implementing the Flash and embedded_storage traits.
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
 | 
					use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
 | 
				
			||||||
use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
 | 
					use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{FirmwareWriter, Partition, State, BOOT_MAGIC, SWAP_MAGIC};
 | 
					use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Errors returned by FirmwareUpdater
 | 
					/// Errors returned by FirmwareUpdater
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
@ -84,10 +84,10 @@ impl FirmwareUpdater {
 | 
				
			|||||||
    /// `mark_booted`.
 | 
					    /// `mark_booted`.
 | 
				
			||||||
    pub async fn get_state<F: AsyncNorFlash>(
 | 
					    pub async fn get_state<F: AsyncNorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        flash: &mut F,
 | 
					        state_flash: &mut F,
 | 
				
			||||||
        aligned: &mut [u8],
 | 
					        aligned: &mut [u8],
 | 
				
			||||||
    ) -> Result<State, FirmwareUpdaterError> {
 | 
					    ) -> Result<State, FirmwareUpdaterError> {
 | 
				
			||||||
        flash.read(self.state.from as u32, aligned).await?;
 | 
					        self.state.read(state_flash, 0, aligned).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
 | 
					        if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
 | 
				
			||||||
            Ok(State::Swap)
 | 
					            Ok(State::Swap)
 | 
				
			||||||
@ -115,17 +115,16 @@ impl FirmwareUpdater {
 | 
				
			|||||||
    #[cfg(feature = "_verify")]
 | 
					    #[cfg(feature = "_verify")]
 | 
				
			||||||
    pub async fn verify_and_mark_updated<F: AsyncNorFlash>(
 | 
					    pub async fn verify_and_mark_updated<F: AsyncNorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        _flash: &mut F,
 | 
					        _state_and_dfu_flash: &mut F,
 | 
				
			||||||
        _public_key: &[u8],
 | 
					        _public_key: &[u8],
 | 
				
			||||||
        _signature: &[u8],
 | 
					        _signature: &[u8],
 | 
				
			||||||
        _update_len: usize,
 | 
					        _update_len: usize,
 | 
				
			||||||
        _aligned: &mut [u8],
 | 
					        _aligned: &mut [u8],
 | 
				
			||||||
    ) -> Result<(), FirmwareUpdaterError> {
 | 
					    ) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        let _end = self.dfu.from + _update_len;
 | 
					 | 
				
			||||||
        let _read_size = _aligned.len();
 | 
					        let _read_size = _aligned.len();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert_eq!(_aligned.len(), F::WRITE_SIZE);
 | 
					        assert_eq!(_aligned.len(), F::WRITE_SIZE);
 | 
				
			||||||
        assert!(_end <= self.dfu.to);
 | 
					        assert!(_update_len <= self.dfu.len());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[cfg(feature = "ed25519-dalek")]
 | 
					        #[cfg(feature = "ed25519-dalek")]
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -137,21 +136,10 @@ impl FirmwareUpdater {
 | 
				
			|||||||
            let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
 | 
					            let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let mut digest = Sha512::new();
 | 
					            let mut digest = Sha512::new();
 | 
				
			||||||
 | 
					            for offset in (0.._update_len).step_by(_aligned.len()) {
 | 
				
			||||||
            let mut offset = self.dfu.from;
 | 
					                self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?;
 | 
				
			||||||
            let last_offset = _end / _read_size * _read_size;
 | 
					                let len = core::cmp::min(_update_len - offset, _aligned.len());
 | 
				
			||||||
 | 
					                digest.update(&_aligned[..len]);
 | 
				
			||||||
            while offset < last_offset {
 | 
					 | 
				
			||||||
                _flash.read(offset as u32, _aligned).await?;
 | 
					 | 
				
			||||||
                digest.update(&_aligned);
 | 
					 | 
				
			||||||
                offset += _read_size;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let remaining = _end % _read_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if remaining > 0 {
 | 
					 | 
				
			||||||
                _flash.read(last_offset as u32, _aligned).await?;
 | 
					 | 
				
			||||||
                digest.update(&_aligned[0..remaining]);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public_key
 | 
					            public_key
 | 
				
			||||||
@ -173,21 +161,10 @@ impl FirmwareUpdater {
 | 
				
			|||||||
            let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
 | 
					            let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let mut digest = Sha512::new();
 | 
					            let mut digest = Sha512::new();
 | 
				
			||||||
 | 
					            for offset in (0.._update_len).step_by(_aligned.len()) {
 | 
				
			||||||
            let mut offset = self.dfu.from;
 | 
					                self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?;
 | 
				
			||||||
            let last_offset = _end / _read_size * _read_size;
 | 
					                let len = core::cmp::min(_update_len - offset, _aligned.len());
 | 
				
			||||||
 | 
					                digest.update(&_aligned[..len]);
 | 
				
			||||||
            while offset < last_offset {
 | 
					 | 
				
			||||||
                _flash.read(offset as u32, _aligned).await?;
 | 
					 | 
				
			||||||
                digest.update(&_aligned);
 | 
					 | 
				
			||||||
                offset += _read_size;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let remaining = _end % _read_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if remaining > 0 {
 | 
					 | 
				
			||||||
                _flash.read(last_offset as u32, _aligned).await?;
 | 
					 | 
				
			||||||
                digest.update(&_aligned[0..remaining]);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let message = digest.finalize();
 | 
					            let message = digest.finalize();
 | 
				
			||||||
@ -202,7 +179,7 @@ impl FirmwareUpdater {
 | 
				
			|||||||
            r.map_err(into_signature_error)?
 | 
					            r.map_err(into_signature_error)?
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.set_magic(_aligned, SWAP_MAGIC, _flash).await
 | 
					        self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Mark to trigger firmware swap on next boot.
 | 
					    /// Mark to trigger firmware swap on next boot.
 | 
				
			||||||
@ -213,11 +190,11 @@ impl FirmwareUpdater {
 | 
				
			|||||||
    #[cfg(not(feature = "_verify"))]
 | 
					    #[cfg(not(feature = "_verify"))]
 | 
				
			||||||
    pub async fn mark_updated<F: AsyncNorFlash>(
 | 
					    pub async fn mark_updated<F: AsyncNorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        flash: &mut F,
 | 
					        state_flash: &mut F,
 | 
				
			||||||
        aligned: &mut [u8],
 | 
					        aligned: &mut [u8],
 | 
				
			||||||
    ) -> Result<(), FirmwareUpdaterError> {
 | 
					    ) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        assert_eq!(aligned.len(), F::WRITE_SIZE);
 | 
					        assert_eq!(aligned.len(), F::WRITE_SIZE);
 | 
				
			||||||
        self.set_magic(aligned, SWAP_MAGIC, flash).await
 | 
					        self.set_magic(aligned, SWAP_MAGIC, state_flash).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Mark firmware boot successful and stop rollback on reset.
 | 
					    /// Mark firmware boot successful and stop rollback on reset.
 | 
				
			||||||
@ -227,29 +204,42 @@ impl FirmwareUpdater {
 | 
				
			|||||||
    /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
 | 
					    /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
 | 
				
			||||||
    pub async fn mark_booted<F: AsyncNorFlash>(
 | 
					    pub async fn mark_booted<F: AsyncNorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        flash: &mut F,
 | 
					        state_flash: &mut F,
 | 
				
			||||||
        aligned: &mut [u8],
 | 
					        aligned: &mut [u8],
 | 
				
			||||||
    ) -> Result<(), FirmwareUpdaterError> {
 | 
					    ) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        assert_eq!(aligned.len(), F::WRITE_SIZE);
 | 
					        assert_eq!(aligned.len(), F::WRITE_SIZE);
 | 
				
			||||||
        self.set_magic(aligned, BOOT_MAGIC, flash).await
 | 
					        self.set_magic(aligned, BOOT_MAGIC, state_flash).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn set_magic<F: AsyncNorFlash>(
 | 
					    async fn set_magic<F: AsyncNorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        aligned: &mut [u8],
 | 
					        aligned: &mut [u8],
 | 
				
			||||||
        magic: u8,
 | 
					        magic: u8,
 | 
				
			||||||
        flash: &mut F,
 | 
					        state_flash: &mut F,
 | 
				
			||||||
    ) -> Result<(), FirmwareUpdaterError> {
 | 
					    ) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        flash.read(self.state.from as u32, aligned).await?;
 | 
					        self.state.read(state_flash, 0, aligned).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if aligned.iter().any(|&b| b != magic) {
 | 
					        if aligned.iter().any(|&b| b != magic) {
 | 
				
			||||||
            aligned.fill(0);
 | 
					            // Read progress validity
 | 
				
			||||||
 | 
					            self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            flash.write(self.state.from as u32, aligned).await?;
 | 
					            // FIXME: Do not make this assumption.
 | 
				
			||||||
            flash.erase(self.state.from as u32, self.state.to as u32).await?;
 | 
					            const STATE_ERASE_VALUE: u8 = 0xFF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
 | 
				
			||||||
 | 
					                // The current progress validity marker is invalid
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Invalidate progress
 | 
				
			||||||
 | 
					                aligned.fill(!STATE_ERASE_VALUE);
 | 
				
			||||||
 | 
					                self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Clear magic and progress
 | 
				
			||||||
 | 
					            self.state.wipe(state_flash).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Set magic
 | 
				
			||||||
            aligned.fill(magic);
 | 
					            aligned.fill(magic);
 | 
				
			||||||
            flash.write(self.state.from as u32, aligned).await?;
 | 
					            self.state.write(state_flash, 0, aligned).await?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -265,45 +255,31 @@ impl FirmwareUpdater {
 | 
				
			|||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        offset: usize,
 | 
					        offset: usize,
 | 
				
			||||||
        data: &[u8],
 | 
					        data: &[u8],
 | 
				
			||||||
        flash: &mut F,
 | 
					        dfu_flash: &mut F,
 | 
				
			||||||
        block_size: usize,
 | 
					 | 
				
			||||||
    ) -> Result<(), FirmwareUpdaterError> {
 | 
					    ) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        assert!(data.len() >= F::ERASE_SIZE);
 | 
					        assert!(data.len() >= F::ERASE_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        flash
 | 
					        self.dfu
 | 
				
			||||||
            .erase(
 | 
					            .erase(dfu_flash, offset as u32, (offset + data.len()) as u32)
 | 
				
			||||||
                (self.dfu.from + offset) as u32,
 | 
					 | 
				
			||||||
                (self.dfu.from + offset + data.len()) as u32,
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            .await?;
 | 
					            .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        trace!(
 | 
					        self.dfu.write(dfu_flash, offset as u32, data).await?;
 | 
				
			||||||
            "Erased from {} to {}",
 | 
					 | 
				
			||||||
            self.dfu.from + offset,
 | 
					 | 
				
			||||||
            self.dfu.from + offset + data.len()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        FirmwareWriter(self.dfu)
 | 
					 | 
				
			||||||
            .write_block(offset, data, flash, block_size)
 | 
					 | 
				
			||||||
            .await?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Prepare for an incoming DFU update by erasing the entire DFU area and
 | 
					    /// Prepare for an incoming DFU update by erasing the entire DFU area and
 | 
				
			||||||
    /// returning a `FirmwareWriter`.
 | 
					    /// returning its `Partition`.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// Using this instead of `write_firmware` allows for an optimized API in
 | 
					    /// Using this instead of `write_firmware` allows for an optimized API in
 | 
				
			||||||
    /// exchange for added complexity.
 | 
					    /// exchange for added complexity.
 | 
				
			||||||
    pub async fn prepare_update<F: AsyncNorFlash>(
 | 
					    pub async fn prepare_update<F: AsyncNorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        flash: &mut F,
 | 
					        dfu_flash: &mut F,
 | 
				
			||||||
    ) -> Result<FirmwareWriter, FirmwareUpdaterError> {
 | 
					    ) -> Result<Partition, FirmwareUpdaterError> {
 | 
				
			||||||
        flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?;
 | 
					        self.dfu.wipe(dfu_flash).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
 | 
					        Ok(self.dfu)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(FirmwareWriter(self.dfu))
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
@ -317,10 +293,10 @@ impl FirmwareUpdater {
 | 
				
			|||||||
    /// `mark_booted`.
 | 
					    /// `mark_booted`.
 | 
				
			||||||
    pub fn get_state_blocking<F: NorFlash>(
 | 
					    pub fn get_state_blocking<F: NorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        flash: &mut F,
 | 
					        state_flash: &mut F,
 | 
				
			||||||
        aligned: &mut [u8],
 | 
					        aligned: &mut [u8],
 | 
				
			||||||
    ) -> Result<State, FirmwareUpdaterError> {
 | 
					    ) -> Result<State, FirmwareUpdaterError> {
 | 
				
			||||||
        flash.read(self.state.from as u32, aligned)?;
 | 
					        self.state.read_blocking(state_flash, 0, aligned)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
 | 
					        if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
 | 
				
			||||||
            Ok(State::Swap)
 | 
					            Ok(State::Swap)
 | 
				
			||||||
@ -348,7 +324,7 @@ impl FirmwareUpdater {
 | 
				
			|||||||
    #[cfg(feature = "_verify")]
 | 
					    #[cfg(feature = "_verify")]
 | 
				
			||||||
    pub fn verify_and_mark_updated_blocking<F: NorFlash>(
 | 
					    pub fn verify_and_mark_updated_blocking<F: NorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        _flash: &mut F,
 | 
					        _state_and_dfu_flash: &mut F,
 | 
				
			||||||
        _public_key: &[u8],
 | 
					        _public_key: &[u8],
 | 
				
			||||||
        _signature: &[u8],
 | 
					        _signature: &[u8],
 | 
				
			||||||
        _update_len: usize,
 | 
					        _update_len: usize,
 | 
				
			||||||
@ -370,21 +346,10 @@ impl FirmwareUpdater {
 | 
				
			|||||||
            let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
 | 
					            let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let mut digest = Sha512::new();
 | 
					            let mut digest = Sha512::new();
 | 
				
			||||||
 | 
					            for offset in (0.._update_len).step_by(_aligned.len()) {
 | 
				
			||||||
            let mut offset = self.dfu.from;
 | 
					                self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?;
 | 
				
			||||||
            let last_offset = _end / _read_size * _read_size;
 | 
					                let len = core::cmp::min(_update_len - offset, _aligned.len());
 | 
				
			||||||
 | 
					                digest.update(&_aligned[..len]);
 | 
				
			||||||
            while offset < last_offset {
 | 
					 | 
				
			||||||
                _flash.read(offset as u32, _aligned)?;
 | 
					 | 
				
			||||||
                digest.update(&_aligned);
 | 
					 | 
				
			||||||
                offset += _read_size;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let remaining = _end % _read_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if remaining > 0 {
 | 
					 | 
				
			||||||
                _flash.read(last_offset as u32, _aligned)?;
 | 
					 | 
				
			||||||
                digest.update(&_aligned[0..remaining]);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public_key
 | 
					            public_key
 | 
				
			||||||
@ -406,21 +371,10 @@ impl FirmwareUpdater {
 | 
				
			|||||||
            let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
 | 
					            let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let mut digest = Sha512::new();
 | 
					            let mut digest = Sha512::new();
 | 
				
			||||||
 | 
					            for offset in (0.._update_len).step_by(_aligned.len()) {
 | 
				
			||||||
            let mut offset = self.dfu.from;
 | 
					                self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?;
 | 
				
			||||||
            let last_offset = _end / _read_size * _read_size;
 | 
					                let len = core::cmp::min(_update_len - offset, _aligned.len());
 | 
				
			||||||
 | 
					                digest.update(&_aligned[..len]);
 | 
				
			||||||
            while offset < last_offset {
 | 
					 | 
				
			||||||
                _flash.read(offset as u32, _aligned)?;
 | 
					 | 
				
			||||||
                digest.update(&_aligned);
 | 
					 | 
				
			||||||
                offset += _read_size;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let remaining = _end % _read_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if remaining > 0 {
 | 
					 | 
				
			||||||
                _flash.read(last_offset as u32, _aligned)?;
 | 
					 | 
				
			||||||
                digest.update(&_aligned[0..remaining]);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let message = digest.finalize();
 | 
					            let message = digest.finalize();
 | 
				
			||||||
@ -435,7 +389,7 @@ impl FirmwareUpdater {
 | 
				
			|||||||
            r.map_err(into_signature_error)?
 | 
					            r.map_err(into_signature_error)?
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash)
 | 
					        self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Mark to trigger firmware swap on next boot.
 | 
					    /// Mark to trigger firmware swap on next boot.
 | 
				
			||||||
@ -446,11 +400,11 @@ impl FirmwareUpdater {
 | 
				
			|||||||
    #[cfg(not(feature = "_verify"))]
 | 
					    #[cfg(not(feature = "_verify"))]
 | 
				
			||||||
    pub fn mark_updated_blocking<F: NorFlash>(
 | 
					    pub fn mark_updated_blocking<F: NorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        flash: &mut F,
 | 
					        state_flash: &mut F,
 | 
				
			||||||
        aligned: &mut [u8],
 | 
					        aligned: &mut [u8],
 | 
				
			||||||
    ) -> Result<(), FirmwareUpdaterError> {
 | 
					    ) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        assert_eq!(aligned.len(), F::WRITE_SIZE);
 | 
					        assert_eq!(aligned.len(), F::WRITE_SIZE);
 | 
				
			||||||
        self.set_magic_blocking(aligned, SWAP_MAGIC, flash)
 | 
					        self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Mark firmware boot successful and stop rollback on reset.
 | 
					    /// Mark firmware boot successful and stop rollback on reset.
 | 
				
			||||||
@ -460,29 +414,42 @@ impl FirmwareUpdater {
 | 
				
			|||||||
    /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
 | 
					    /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
 | 
				
			||||||
    pub fn mark_booted_blocking<F: NorFlash>(
 | 
					    pub fn mark_booted_blocking<F: NorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        flash: &mut F,
 | 
					        state_flash: &mut F,
 | 
				
			||||||
        aligned: &mut [u8],
 | 
					        aligned: &mut [u8],
 | 
				
			||||||
    ) -> Result<(), FirmwareUpdaterError> {
 | 
					    ) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        assert_eq!(aligned.len(), F::WRITE_SIZE);
 | 
					        assert_eq!(aligned.len(), F::WRITE_SIZE);
 | 
				
			||||||
        self.set_magic_blocking(aligned, BOOT_MAGIC, flash)
 | 
					        self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn set_magic_blocking<F: NorFlash>(
 | 
					    fn set_magic_blocking<F: NorFlash>(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        aligned: &mut [u8],
 | 
					        aligned: &mut [u8],
 | 
				
			||||||
        magic: u8,
 | 
					        magic: u8,
 | 
				
			||||||
        flash: &mut F,
 | 
					        state_flash: &mut F,
 | 
				
			||||||
    ) -> Result<(), FirmwareUpdaterError> {
 | 
					    ) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        flash.read(self.state.from as u32, aligned)?;
 | 
					        self.state.read_blocking(state_flash, 0, aligned)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if aligned.iter().any(|&b| b != magic) {
 | 
					        if aligned.iter().any(|&b| b != magic) {
 | 
				
			||||||
            aligned.fill(0);
 | 
					            // Read progress validity
 | 
				
			||||||
 | 
					            self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            flash.write(self.state.from as u32, aligned)?;
 | 
					            // FIXME: Do not make this assumption.
 | 
				
			||||||
            flash.erase(self.state.from as u32, self.state.to as u32)?;
 | 
					            const STATE_ERASE_VALUE: u8 = 0xFF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
 | 
				
			||||||
 | 
					                // The current progress validity marker is invalid
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Invalidate progress
 | 
				
			||||||
 | 
					                aligned.fill(!STATE_ERASE_VALUE);
 | 
				
			||||||
 | 
					                self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Clear magic and progress
 | 
				
			||||||
 | 
					            self.state.wipe_blocking(state_flash)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Set magic
 | 
				
			||||||
            aligned.fill(magic);
 | 
					            aligned.fill(magic);
 | 
				
			||||||
            flash.write(self.state.from as u32, aligned)?;
 | 
					            self.state.write_blocking(state_flash, 0, aligned)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -498,40 +465,26 @@ impl FirmwareUpdater {
 | 
				
			|||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        offset: usize,
 | 
					        offset: usize,
 | 
				
			||||||
        data: &[u8],
 | 
					        data: &[u8],
 | 
				
			||||||
        flash: &mut F,
 | 
					        dfu_flash: &mut F,
 | 
				
			||||||
        block_size: usize,
 | 
					 | 
				
			||||||
    ) -> Result<(), FirmwareUpdaterError> {
 | 
					    ) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        assert!(data.len() >= F::ERASE_SIZE);
 | 
					        assert!(data.len() >= F::ERASE_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        flash.erase(
 | 
					        self.dfu
 | 
				
			||||||
            (self.dfu.from + offset) as u32,
 | 
					            .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?;
 | 
				
			||||||
            (self.dfu.from + offset + data.len()) as u32,
 | 
					 | 
				
			||||||
        )?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        trace!(
 | 
					        self.dfu.write_blocking(dfu_flash, offset as u32, data)?;
 | 
				
			||||||
            "Erased from {} to {}",
 | 
					 | 
				
			||||||
            self.dfu.from + offset,
 | 
					 | 
				
			||||||
            self.dfu.from + offset + data.len()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Prepare for an incoming DFU update by erasing the entire DFU area and
 | 
					    /// Prepare for an incoming DFU update by erasing the entire DFU area and
 | 
				
			||||||
    /// returning a `FirmwareWriter`.
 | 
					    /// returning its `Partition`.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// Using this instead of `write_firmware_blocking` allows for an optimized
 | 
					    /// Using this instead of `write_firmware_blocking` allows for an optimized
 | 
				
			||||||
    /// API in exchange for added complexity.
 | 
					    /// API in exchange for added complexity.
 | 
				
			||||||
    pub fn prepare_update_blocking<F: NorFlash>(
 | 
					    pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<Partition, FirmwareUpdaterError> {
 | 
				
			||||||
        &mut self,
 | 
					        self.dfu.wipe_blocking(flash)?;
 | 
				
			||||||
        flash: &mut F,
 | 
					 | 
				
			||||||
    ) -> Result<FirmwareWriter, FirmwareUpdaterError> {
 | 
					 | 
				
			||||||
        flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
 | 
					        Ok(self.dfu)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(FirmwareWriter(self.dfu))
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,97 +0,0 @@
 | 
				
			|||||||
use embedded_storage::nor_flash::NorFlash;
 | 
					 | 
				
			||||||
use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use crate::Partition;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// FirmwareWriter allows writing blocks to an already erased flash.
 | 
					 | 
				
			||||||
pub struct FirmwareWriter(pub(crate) Partition);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl FirmwareWriter {
 | 
					 | 
				
			||||||
    /// Write data to a flash page.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// # Safety
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// Failing to meet alignment and size requirements may result in a panic.
 | 
					 | 
				
			||||||
    pub async fn write_block<F: AsyncNorFlash>(
 | 
					 | 
				
			||||||
        &mut self,
 | 
					 | 
				
			||||||
        offset: usize,
 | 
					 | 
				
			||||||
        data: &[u8],
 | 
					 | 
				
			||||||
        flash: &mut F,
 | 
					 | 
				
			||||||
        block_size: usize,
 | 
					 | 
				
			||||||
    ) -> Result<(), F::Error> {
 | 
					 | 
				
			||||||
        trace!(
 | 
					 | 
				
			||||||
            "Writing firmware at offset 0x{:x} len {}",
 | 
					 | 
				
			||||||
            self.0.from + offset,
 | 
					 | 
				
			||||||
            data.len()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut write_offset = self.0.from + offset;
 | 
					 | 
				
			||||||
        for chunk in data.chunks(block_size) {
 | 
					 | 
				
			||||||
            trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
 | 
					 | 
				
			||||||
            flash.write(write_offset as u32, chunk).await?;
 | 
					 | 
				
			||||||
            write_offset += chunk.len();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        /*
 | 
					 | 
				
			||||||
        trace!("Wrote data, reading back for verification");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut buf: [u8; 4096] = [0; 4096];
 | 
					 | 
				
			||||||
        let mut data_offset = 0;
 | 
					 | 
				
			||||||
        let mut read_offset = self.dfu.from + offset;
 | 
					 | 
				
			||||||
        for chunk in buf.chunks_mut(block_size) {
 | 
					 | 
				
			||||||
            flash.read(read_offset as u32, chunk).await?;
 | 
					 | 
				
			||||||
            trace!("Read chunk at {}: {:?}", read_offset, chunk);
 | 
					 | 
				
			||||||
            assert_eq!(&data[data_offset..data_offset + block_size], chunk);
 | 
					 | 
				
			||||||
            read_offset += chunk.len();
 | 
					 | 
				
			||||||
            data_offset += chunk.len();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Write data to a flash page.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// # Safety
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// Failing to meet alignment and size requirements may result in a panic.
 | 
					 | 
				
			||||||
    pub fn write_block_blocking<F: NorFlash>(
 | 
					 | 
				
			||||||
        &mut self,
 | 
					 | 
				
			||||||
        offset: usize,
 | 
					 | 
				
			||||||
        data: &[u8],
 | 
					 | 
				
			||||||
        flash: &mut F,
 | 
					 | 
				
			||||||
        block_size: usize,
 | 
					 | 
				
			||||||
    ) -> Result<(), F::Error> {
 | 
					 | 
				
			||||||
        trace!(
 | 
					 | 
				
			||||||
            "Writing firmware at offset 0x{:x} len {}",
 | 
					 | 
				
			||||||
            self.0.from + offset,
 | 
					 | 
				
			||||||
            data.len()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut write_offset = self.0.from + offset;
 | 
					 | 
				
			||||||
        for chunk in data.chunks(block_size) {
 | 
					 | 
				
			||||||
            trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
 | 
					 | 
				
			||||||
            flash.write(write_offset as u32, chunk)?;
 | 
					 | 
				
			||||||
            write_offset += chunk.len();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        /*
 | 
					 | 
				
			||||||
        trace!("Wrote data, reading back for verification");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut buf: [u8; 4096] = [0; 4096];
 | 
					 | 
				
			||||||
        let mut data_offset = 0;
 | 
					 | 
				
			||||||
        let mut read_offset = self.dfu.from + offset;
 | 
					 | 
				
			||||||
        for chunk in buf.chunks_mut(block_size) {
 | 
					 | 
				
			||||||
            flash.read(read_offset as u32, chunk).await?;
 | 
					 | 
				
			||||||
            trace!("Read chunk at {}: {:?}", read_offset, chunk);
 | 
					 | 
				
			||||||
            assert_eq!(&data[data_offset..data_offset + block_size], chunk);
 | 
					 | 
				
			||||||
            read_offset += chunk.len();
 | 
					 | 
				
			||||||
            data_offset += chunk.len();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -7,12 +7,11 @@ mod fmt;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
mod boot_loader;
 | 
					mod boot_loader;
 | 
				
			||||||
mod firmware_updater;
 | 
					mod firmware_updater;
 | 
				
			||||||
mod firmware_writer;
 | 
					mod mem_flash;
 | 
				
			||||||
mod partition;
 | 
					mod partition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig};
 | 
					pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig};
 | 
				
			||||||
pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError};
 | 
					pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError};
 | 
				
			||||||
pub use firmware_writer::FirmwareWriter;
 | 
					 | 
				
			||||||
pub use partition::Partition;
 | 
					pub use partition::Partition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
 | 
					pub(crate) const BOOT_MAGIC: u8 = 0xD0;
 | 
				
			||||||
@ -46,13 +45,10 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod tests {
 | 
					mod tests {
 | 
				
			||||||
    use core::convert::Infallible;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
 | 
					 | 
				
			||||||
    use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
 | 
					 | 
				
			||||||
    use futures::executor::block_on;
 | 
					    use futures::executor::block_on;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    use crate::mem_flash::MemFlash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
@ -75,8 +71,8 @@ mod tests {
 | 
				
			|||||||
        const ACTIVE: Partition = Partition::new(4096, 61440);
 | 
					        const ACTIVE: Partition = Partition::new(4096, 61440);
 | 
				
			||||||
        const DFU: Partition = Partition::new(61440, 122880);
 | 
					        const DFU: Partition = Partition::new(61440, 122880);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]);
 | 
					        let mut flash = MemFlash::<131072, 4096, 4>::default();
 | 
				
			||||||
        flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
 | 
					        flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
 | 
				
			||||||
        let mut flash = SingleFlashConfig::new(&mut flash);
 | 
					        let mut flash = SingleFlashConfig::new(&mut flash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
 | 
					        let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
 | 
				
			||||||
@ -95,21 +91,21 @@ mod tests {
 | 
				
			|||||||
        const STATE: Partition = Partition::new(0, 4096);
 | 
					        const STATE: Partition = Partition::new(0, 4096);
 | 
				
			||||||
        const ACTIVE: Partition = Partition::new(4096, 61440);
 | 
					        const ACTIVE: Partition = Partition::new(4096, 61440);
 | 
				
			||||||
        const DFU: Partition = Partition::new(61440, 122880);
 | 
					        const DFU: Partition = Partition::new(61440, 122880);
 | 
				
			||||||
        let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]);
 | 
					        let mut flash = MemFlash::<131072, 4096, 4>::random();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
 | 
					        let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
 | 
				
			||||||
        let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
 | 
					        let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
 | 
				
			||||||
        let mut aligned = [0; 4];
 | 
					        let mut aligned = [0; 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for i in ACTIVE.from..ACTIVE.to {
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
            flash.0[i] = original[i - ACTIVE.from];
 | 
					            flash.mem[i] = original[i - ACTIVE.from];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
 | 
					        let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
 | 
				
			||||||
        let mut updater = FirmwareUpdater::new(DFU, STATE);
 | 
					        let mut updater = FirmwareUpdater::new(DFU, STATE);
 | 
				
			||||||
        let mut offset = 0;
 | 
					        let mut offset = 0;
 | 
				
			||||||
        for chunk in update.chunks(4096) {
 | 
					        for chunk in update.chunks(4096) {
 | 
				
			||||||
            block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap();
 | 
					            block_on(updater.write_firmware(offset, chunk, &mut flash)).unwrap();
 | 
				
			||||||
            offset += chunk.len();
 | 
					            offset += chunk.len();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap();
 | 
					        block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap();
 | 
				
			||||||
@ -124,12 +120,12 @@ mod tests {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for i in ACTIVE.from..ACTIVE.to {
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
            assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i);
 | 
					            assert_eq!(flash.mem[i], update[i - ACTIVE.from], "Index {}", i);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // First DFU page is untouched
 | 
					        // First DFU page is untouched
 | 
				
			||||||
        for i in DFU.from + 4096..DFU.to {
 | 
					        for i in DFU.from + 4096..DFU.to {
 | 
				
			||||||
            assert_eq!(flash.0[i], original[i - DFU.from - 4096], "Index {}", i);
 | 
					            assert_eq!(flash.mem[i], original[i - DFU.from - 4096], "Index {}", i);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Running again should cause a revert
 | 
					        // Running again should cause a revert
 | 
				
			||||||
@ -141,12 +137,12 @@ mod tests {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for i in ACTIVE.from..ACTIVE.to {
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
            assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i);
 | 
					            assert_eq!(flash.mem[i], original[i - ACTIVE.from], "Index {}", i);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Last page is untouched
 | 
					        // Last page is untouched
 | 
				
			||||||
        for i in DFU.from..DFU.to - 4096 {
 | 
					        for i in DFU.from..DFU.to - 4096 {
 | 
				
			||||||
            assert_eq!(flash.0[i], update[i - DFU.from], "Index {}", i);
 | 
					            assert_eq!(flash.mem[i], update[i - DFU.from], "Index {}", i);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Mark as booted
 | 
					        // Mark as booted
 | 
				
			||||||
@ -166,23 +162,23 @@ mod tests {
 | 
				
			|||||||
        const ACTIVE: Partition = Partition::new(4096, 16384);
 | 
					        const ACTIVE: Partition = Partition::new(4096, 16384);
 | 
				
			||||||
        const DFU: Partition = Partition::new(0, 16384);
 | 
					        const DFU: Partition = Partition::new(0, 16384);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]);
 | 
					        let mut active = MemFlash::<16384, 4096, 8>::random();
 | 
				
			||||||
        let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]);
 | 
					        let mut dfu = MemFlash::<16384, 2048, 8>::random();
 | 
				
			||||||
        let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]);
 | 
					        let mut state = MemFlash::<4096, 128, 4>::random();
 | 
				
			||||||
        let mut aligned = [0; 4];
 | 
					        let mut aligned = [0; 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
 | 
					        let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
 | 
				
			||||||
        let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
 | 
					        let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for i in ACTIVE.from..ACTIVE.to {
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
            active.0[i] = original[i - ACTIVE.from];
 | 
					            active.mem[i] = original[i - ACTIVE.from];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut updater = FirmwareUpdater::new(DFU, STATE);
 | 
					        let mut updater = FirmwareUpdater::new(DFU, STATE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut offset = 0;
 | 
					        let mut offset = 0;
 | 
				
			||||||
        for chunk in update.chunks(2048) {
 | 
					        for chunk in update.chunks(2048) {
 | 
				
			||||||
            block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap();
 | 
					            block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap();
 | 
				
			||||||
            offset += chunk.len();
 | 
					            offset += chunk.len();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
 | 
					        block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
 | 
				
			||||||
@ -203,12 +199,12 @@ mod tests {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for i in ACTIVE.from..ACTIVE.to {
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
            assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i);
 | 
					            assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // First DFU page is untouched
 | 
					        // First DFU page is untouched
 | 
				
			||||||
        for i in DFU.from + 4096..DFU.to {
 | 
					        for i in DFU.from + 4096..DFU.to {
 | 
				
			||||||
            assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i);
 | 
					            assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -220,22 +216,22 @@ mod tests {
 | 
				
			|||||||
        const DFU: Partition = Partition::new(0, 16384);
 | 
					        const DFU: Partition = Partition::new(0, 16384);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut aligned = [0; 4];
 | 
					        let mut aligned = [0; 4];
 | 
				
			||||||
        let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]);
 | 
					        let mut active = MemFlash::<16384, 2048, 4>::random();
 | 
				
			||||||
        let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]);
 | 
					        let mut dfu = MemFlash::<16384, 4096, 8>::random();
 | 
				
			||||||
        let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]);
 | 
					        let mut state = MemFlash::<4096, 128, 4>::random();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
 | 
					        let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
 | 
				
			||||||
        let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
 | 
					        let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for i in ACTIVE.from..ACTIVE.to {
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
            active.0[i] = original[i - ACTIVE.from];
 | 
					            active.mem[i] = original[i - ACTIVE.from];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut updater = FirmwareUpdater::new(DFU, STATE);
 | 
					        let mut updater = FirmwareUpdater::new(DFU, STATE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut offset = 0;
 | 
					        let mut offset = 0;
 | 
				
			||||||
        for chunk in update.chunks(4096) {
 | 
					        for chunk in update.chunks(4096) {
 | 
				
			||||||
            block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap();
 | 
					            block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap();
 | 
				
			||||||
            offset += chunk.len();
 | 
					            offset += chunk.len();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
 | 
					        block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
 | 
				
			||||||
@ -255,12 +251,12 @@ mod tests {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for i in ACTIVE.from..ACTIVE.to {
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
            assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i);
 | 
					            assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // First DFU page is untouched
 | 
					        // First DFU page is untouched
 | 
				
			||||||
        for i in DFU.from + 4096..DFU.to {
 | 
					        for i in DFU.from + 4096..DFU.to {
 | 
				
			||||||
            assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i);
 | 
					            assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -290,13 +286,13 @@ mod tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const STATE: Partition = Partition::new(0, 4096);
 | 
					        const STATE: Partition = Partition::new(0, 4096);
 | 
				
			||||||
        const DFU: Partition = Partition::new(4096, 8192);
 | 
					        const DFU: Partition = Partition::new(4096, 8192);
 | 
				
			||||||
        let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]);
 | 
					        let mut flash = MemFlash::<8192, 4096, 4>::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let firmware_len = firmware.len();
 | 
					        let firmware_len = firmware.len();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut write_buf = [0; 4096];
 | 
					        let mut write_buf = [0; 4096];
 | 
				
			||||||
        write_buf[0..firmware_len].copy_from_slice(firmware);
 | 
					        write_buf[0..firmware_len].copy_from_slice(firmware);
 | 
				
			||||||
        NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap();
 | 
					        DFU.write_blocking(&mut flash, 0, &write_buf).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // On with the test
 | 
					        // On with the test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -313,112 +309,4 @@ mod tests {
 | 
				
			|||||||
        ))
 | 
					        ))
 | 
				
			||||||
        .is_ok());
 | 
					        .is_ok());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
 | 
					 | 
				
			||||||
        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        const WRITE_SIZE: usize = WRITE_SIZE;
 | 
					 | 
				
			||||||
        const ERASE_SIZE: usize = ERASE_SIZE;
 | 
					 | 
				
			||||||
        fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
 | 
					 | 
				
			||||||
            let from = from as usize;
 | 
					 | 
				
			||||||
            let to = to as usize;
 | 
					 | 
				
			||||||
            assert!(from % ERASE_SIZE == 0);
 | 
					 | 
				
			||||||
            assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
 | 
					 | 
				
			||||||
            for i in from..to {
 | 
					 | 
				
			||||||
                self.0[i] = 0xFF;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Ok(())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
 | 
					 | 
				
			||||||
            assert!(data.len() % WRITE_SIZE == 0);
 | 
					 | 
				
			||||||
            assert!(offset as usize % WRITE_SIZE == 0);
 | 
					 | 
				
			||||||
            assert!(offset as usize + data.len() <= SIZE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Ok(())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
 | 
					 | 
				
			||||||
        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        type Error = Infallible;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
 | 
					 | 
				
			||||||
        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        const READ_SIZE: usize = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
 | 
					 | 
				
			||||||
            let len = buf.len();
 | 
					 | 
				
			||||||
            buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
 | 
					 | 
				
			||||||
            Ok(())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fn capacity(&self) -> usize {
 | 
					 | 
				
			||||||
            SIZE
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> super::Flash
 | 
					 | 
				
			||||||
        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        const BLOCK_SIZE: usize = ERASE_SIZE;
 | 
					 | 
				
			||||||
        const ERASE_VALUE: u8 = 0xFF;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
 | 
					 | 
				
			||||||
        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        const READ_SIZE: usize = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        async fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
 | 
					 | 
				
			||||||
            let len = buf.len();
 | 
					 | 
				
			||||||
            buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
 | 
					 | 
				
			||||||
            Ok(())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fn capacity(&self) -> usize {
 | 
					 | 
				
			||||||
            SIZE
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
 | 
					 | 
				
			||||||
        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        const WRITE_SIZE: usize = WRITE_SIZE;
 | 
					 | 
				
			||||||
        const ERASE_SIZE: usize = ERASE_SIZE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
 | 
					 | 
				
			||||||
            let from = from as usize;
 | 
					 | 
				
			||||||
            let to = to as usize;
 | 
					 | 
				
			||||||
            assert!(from % ERASE_SIZE == 0);
 | 
					 | 
				
			||||||
            assert!(to % ERASE_SIZE == 0);
 | 
					 | 
				
			||||||
            for i in from..to {
 | 
					 | 
				
			||||||
                self.0[i] = 0xFF;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Ok(())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
 | 
					 | 
				
			||||||
            info!("Writing {} bytes to 0x{:x}", data.len(), offset);
 | 
					 | 
				
			||||||
            assert!(data.len() % WRITE_SIZE == 0);
 | 
					 | 
				
			||||||
            assert!(offset as usize % WRITE_SIZE == 0);
 | 
					 | 
				
			||||||
            assert!(
 | 
					 | 
				
			||||||
                offset as usize + data.len() <= SIZE,
 | 
					 | 
				
			||||||
                "OFFSET: {}, LEN: {}, FLASH SIZE: {}",
 | 
					 | 
				
			||||||
                offset,
 | 
					 | 
				
			||||||
                data.len(),
 | 
					 | 
				
			||||||
                SIZE
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Ok(())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										156
									
								
								embassy-boot/boot/src/mem_flash.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								embassy-boot/boot/src/mem_flash.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,156 @@
 | 
				
			|||||||
 | 
					#![allow(unused)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use core::ops::{Bound, Range, RangeBounds};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
 | 
				
			||||||
 | 
					use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::Flash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> {
 | 
				
			||||||
 | 
					    pub mem: [u8; SIZE],
 | 
				
			||||||
 | 
					    pub pending_write_successes: Option<usize>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct MemFlashError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> {
 | 
				
			||||||
 | 
					    pub const fn new(fill: u8) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            mem: [fill; SIZE],
 | 
				
			||||||
 | 
					            pending_write_successes: None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(test)]
 | 
				
			||||||
 | 
					    pub fn random() -> Self {
 | 
				
			||||||
 | 
					        let mut mem = [0; SIZE];
 | 
				
			||||||
 | 
					        for byte in mem.iter_mut() {
 | 
				
			||||||
 | 
					            *byte = rand::random::<u8>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            mem,
 | 
				
			||||||
 | 
					            pending_write_successes: None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Default
 | 
				
			||||||
 | 
					    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::new(0xFF)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Flash
 | 
				
			||||||
 | 
					    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const BLOCK_SIZE: usize = ERASE_SIZE;
 | 
				
			||||||
 | 
					    const ERASE_VALUE: u8 = 0xFF;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
 | 
				
			||||||
 | 
					    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type Error = MemFlashError;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl NorFlashError for MemFlashError {
 | 
				
			||||||
 | 
					    fn kind(&self) -> NorFlashErrorKind {
 | 
				
			||||||
 | 
					        NorFlashErrorKind::Other
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
 | 
				
			||||||
 | 
					    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const READ_SIZE: usize = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        let len = bytes.len();
 | 
				
			||||||
 | 
					        bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn capacity(&self) -> usize {
 | 
				
			||||||
 | 
					        SIZE
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
 | 
				
			||||||
 | 
					    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const WRITE_SIZE: usize = WRITE_SIZE;
 | 
				
			||||||
 | 
					    const ERASE_SIZE: usize = ERASE_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        let from = from as usize;
 | 
				
			||||||
 | 
					        let to = to as usize;
 | 
				
			||||||
 | 
					        assert!(from % ERASE_SIZE == 0);
 | 
				
			||||||
 | 
					        assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
 | 
				
			||||||
 | 
					        for i in from..to {
 | 
				
			||||||
 | 
					            self.mem[i] = 0xFF;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        let offset = offset as usize;
 | 
				
			||||||
 | 
					        assert!(bytes.len() % WRITE_SIZE == 0);
 | 
				
			||||||
 | 
					        assert!(offset % WRITE_SIZE == 0);
 | 
				
			||||||
 | 
					        assert!(offset + bytes.len() <= SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(pending_successes) = self.pending_write_successes {
 | 
				
			||||||
 | 
					            if pending_successes > 0 {
 | 
				
			||||||
 | 
					                self.pending_write_successes = Some(pending_successes - 1);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return Err(MemFlashError);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for ((offset, mem_byte), new_byte) in self
 | 
				
			||||||
 | 
					            .mem
 | 
				
			||||||
 | 
					            .iter_mut()
 | 
				
			||||||
 | 
					            .enumerate()
 | 
				
			||||||
 | 
					            .skip(offset)
 | 
				
			||||||
 | 
					            .take(bytes.len())
 | 
				
			||||||
 | 
					            .zip(bytes)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset);
 | 
				
			||||||
 | 
					            *mem_byte = *new_byte;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
 | 
				
			||||||
 | 
					    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const READ_SIZE: usize = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        <Self as ReadNorFlash>::read(self, offset, bytes)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn capacity(&self) -> usize {
 | 
				
			||||||
 | 
					        <Self as ReadNorFlash>::capacity(self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
 | 
				
			||||||
 | 
					    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const WRITE_SIZE: usize = WRITE_SIZE;
 | 
				
			||||||
 | 
					    const ERASE_SIZE: usize = ERASE_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        <Self as NorFlash>::erase(self, from, to)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        <Self as NorFlash>::write(self, offset, bytes)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,10 +1,13 @@
 | 
				
			|||||||
 | 
					use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
 | 
				
			||||||
 | 
					use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A region in flash used by the bootloader.
 | 
					/// A region in flash used by the bootloader.
 | 
				
			||||||
#[derive(Copy, Clone, Debug)]
 | 
					#[derive(Copy, Clone, Debug)]
 | 
				
			||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
					#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
				
			||||||
pub struct Partition {
 | 
					pub struct Partition {
 | 
				
			||||||
    /// Start of the flash region.
 | 
					    /// The offset into the flash where the partition starts.
 | 
				
			||||||
    pub from: usize,
 | 
					    pub from: usize,
 | 
				
			||||||
    /// End of the flash region.
 | 
					    /// The offset into the flash where the partition ends.
 | 
				
			||||||
    pub to: usize,
 | 
					    pub to: usize,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -14,9 +17,124 @@ impl Partition {
 | 
				
			|||||||
        Self { from, to }
 | 
					        Self { from, to }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Return the length of the partition
 | 
					    /// Return the size of the partition
 | 
				
			||||||
    #[allow(clippy::len_without_is_empty)]
 | 
					    #[allow(clippy::len_without_is_empty)]
 | 
				
			||||||
    pub const fn len(&self) -> usize {
 | 
					    pub const fn len(&self) -> usize {
 | 
				
			||||||
        self.to - self.from
 | 
					        self.to - self.from
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Read from the partition on the provided flash
 | 
				
			||||||
 | 
					    pub async fn read<F: AsyncReadNorFlash>(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        flash: &mut F,
 | 
				
			||||||
 | 
					        offset: u32,
 | 
				
			||||||
 | 
					        bytes: &mut [u8],
 | 
				
			||||||
 | 
					    ) -> Result<(), F::Error> {
 | 
				
			||||||
 | 
					        let offset = self.from as u32 + offset;
 | 
				
			||||||
 | 
					        flash.read(offset, bytes).await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Write to the partition on the provided flash
 | 
				
			||||||
 | 
					    pub async fn write<F: AsyncNorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
 | 
				
			||||||
 | 
					        let offset = self.from as u32 + offset;
 | 
				
			||||||
 | 
					        flash.write(offset, bytes).await?;
 | 
				
			||||||
 | 
					        trace!("Wrote from 0x{:x} len {}", offset, bytes.len());
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Erase part of the partition on the provided flash
 | 
				
			||||||
 | 
					    pub async fn erase<F: AsyncNorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
 | 
				
			||||||
 | 
					        let from = self.from as u32 + from;
 | 
				
			||||||
 | 
					        let to = self.from as u32 + to;
 | 
				
			||||||
 | 
					        flash.erase(from, to).await?;
 | 
				
			||||||
 | 
					        trace!("Erased from 0x{:x} to 0x{:x}", from, to);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Erase the entire partition
 | 
				
			||||||
 | 
					    pub(crate) async fn wipe<F: AsyncNorFlash>(&self, flash: &mut F) -> Result<(), F::Error> {
 | 
				
			||||||
 | 
					        let from = self.from as u32;
 | 
				
			||||||
 | 
					        let to = self.to as u32;
 | 
				
			||||||
 | 
					        flash.erase(from, to).await?;
 | 
				
			||||||
 | 
					        trace!("Wiped from 0x{:x} to 0x{:x}", from, to);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Read from the partition on the provided flash
 | 
				
			||||||
 | 
					    pub fn read_blocking<F: ReadNorFlash>(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> {
 | 
				
			||||||
 | 
					        let offset = self.from as u32 + offset;
 | 
				
			||||||
 | 
					        flash.read(offset, bytes)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Write to the partition on the provided flash
 | 
				
			||||||
 | 
					    pub fn write_blocking<F: NorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
 | 
				
			||||||
 | 
					        let offset = self.from as u32 + offset;
 | 
				
			||||||
 | 
					        flash.write(offset, bytes)?;
 | 
				
			||||||
 | 
					        trace!("Wrote from 0x{:x} len {}", offset, bytes.len());
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Erase part of the partition on the provided flash
 | 
				
			||||||
 | 
					    pub fn erase_blocking<F: NorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
 | 
				
			||||||
 | 
					        let from = self.from as u32 + from;
 | 
				
			||||||
 | 
					        let to = self.from as u32 + to;
 | 
				
			||||||
 | 
					        flash.erase(from, to)?;
 | 
				
			||||||
 | 
					        trace!("Erased from 0x{:x} to 0x{:x}", from, to);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Erase the entire partition
 | 
				
			||||||
 | 
					    pub(crate) fn wipe_blocking<F: NorFlash>(&self, flash: &mut F) -> Result<(), F::Error> {
 | 
				
			||||||
 | 
					        let from = self.from as u32;
 | 
				
			||||||
 | 
					        let to = self.to as u32;
 | 
				
			||||||
 | 
					        flash.erase(from, to)?;
 | 
				
			||||||
 | 
					        trace!("Wiped from 0x{:x} to 0x{:x}", from, to);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use crate::mem_flash::MemFlash;
 | 
				
			||||||
 | 
					    use crate::Partition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn can_erase() {
 | 
				
			||||||
 | 
					        let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
 | 
				
			||||||
 | 
					        let partition = Partition::new(256, 512);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        partition.erase_blocking(&mut flash, 64, 192).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) {
 | 
				
			||||||
 | 
					            assert_eq!(0x00, byte, "Index {}", index);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) {
 | 
				
			||||||
 | 
					            assert_eq!(0xFF, byte, "Index {}", index);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) {
 | 
				
			||||||
 | 
					            assert_eq!(0x00, byte, "Index {}", index);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn can_wipe() {
 | 
				
			||||||
 | 
					        let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
 | 
				
			||||||
 | 
					        let partition = Partition::new(256, 512);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        partition.wipe_blocking(&mut flash).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (index, byte) in flash.mem.iter().copied().enumerate().take(256) {
 | 
				
			||||||
 | 
					            assert_eq!(0x00, byte, "Index {}", index);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) {
 | 
				
			||||||
 | 
					            assert_eq!(0xFF, byte, "Index {}", index);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) {
 | 
				
			||||||
 | 
					            assert_eq!(0x00, byte, "Index {}", index);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,116 +0,0 @@
 | 
				
			|||||||
//! Executor specific to cortex-m devices.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use core::cell::UnsafeCell;
 | 
					 | 
				
			||||||
use core::mem::MaybeUninit;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use atomic_polyfill::{AtomicBool, Ordering};
 | 
					 | 
				
			||||||
use cortex_m::interrupt::InterruptNumber;
 | 
					 | 
				
			||||||
use cortex_m::peripheral::NVIC;
 | 
					 | 
				
			||||||
pub use embassy_executor::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Clone, Copy)]
 | 
					 | 
				
			||||||
struct N(u16);
 | 
					 | 
				
			||||||
unsafe impl cortex_m::interrupt::InterruptNumber for N {
 | 
					 | 
				
			||||||
    fn number(self) -> u16 {
 | 
					 | 
				
			||||||
        self.0
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn pend_by_number(n: u16) {
 | 
					 | 
				
			||||||
    cortex_m::peripheral::NVIC::pend(N(n))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Interrupt mode executor.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// This executor runs tasks in interrupt mode. The interrupt handler is set up
 | 
					 | 
				
			||||||
/// to poll tasks, and when a task is woken the interrupt is pended from software.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// This allows running async tasks at a priority higher than thread mode. One
 | 
					 | 
				
			||||||
/// use case is to leave thread mode free for non-async tasks. Another use case is
 | 
					 | 
				
			||||||
/// to run multiple executors: one in thread mode for low priority tasks and another in
 | 
					 | 
				
			||||||
/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
 | 
					 | 
				
			||||||
/// priority ones.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// It is even possible to run multiple interrupt mode executors at different priorities,
 | 
					 | 
				
			||||||
/// by assigning different priorities to the interrupts. For an example on how to do this,
 | 
					 | 
				
			||||||
/// See the 'multiprio' example for 'embassy-nrf'.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// To use it, you have to pick an interrupt that won't be used by the hardware.
 | 
					 | 
				
			||||||
/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
 | 
					 | 
				
			||||||
/// If this is not the case, you may use an interrupt from any unused peripheral.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// It is somewhat more complex to use, it's recommended to use the thread-mode
 | 
					 | 
				
			||||||
/// [`Executor`] instead, if it works for your use case.
 | 
					 | 
				
			||||||
pub struct InterruptExecutor {
 | 
					 | 
				
			||||||
    started: AtomicBool,
 | 
					 | 
				
			||||||
    executor: UnsafeCell<MaybeUninit<raw::Executor>>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
unsafe impl Send for InterruptExecutor {}
 | 
					 | 
				
			||||||
unsafe impl Sync for InterruptExecutor {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl InterruptExecutor {
 | 
					 | 
				
			||||||
    /// Create a new, not started `InterruptExecutor`.
 | 
					 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub const fn new() -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            started: AtomicBool::new(false),
 | 
					 | 
				
			||||||
            executor: UnsafeCell::new(MaybeUninit::uninit()),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Executor interrupt callback.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// # Safety
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// You MUST call this from the interrupt handler, and from nowhere else.
 | 
					 | 
				
			||||||
    pub unsafe fn on_interrupt(&'static self) {
 | 
					 | 
				
			||||||
        let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
 | 
					 | 
				
			||||||
        executor.poll();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Start the executor.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This initializes the executor, enables the interrupt, and returns.
 | 
					 | 
				
			||||||
    /// The executor keeps running in the background through the interrupt.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
 | 
					 | 
				
			||||||
    /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a
 | 
					 | 
				
			||||||
    /// different "thread" (the interrupt), so spawning tasks on it is effectively
 | 
					 | 
				
			||||||
    /// sending them.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from
 | 
					 | 
				
			||||||
    /// a task running in it.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// # Interrupt requirements
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// You must set the interrupt priority before calling this method. You MUST NOT
 | 
					 | 
				
			||||||
    /// do it after.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    pub fn start(&'static self, irq: impl InterruptNumber) -> SendSpawner {
 | 
					 | 
				
			||||||
        if self
 | 
					 | 
				
			||||||
            .started
 | 
					 | 
				
			||||||
            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
 | 
					 | 
				
			||||||
            .is_err()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            panic!("InterruptExecutor::start() called multiple times on the same executor.");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        unsafe {
 | 
					 | 
				
			||||||
            (&mut *self.executor.get()).as_mut_ptr().write(raw::Executor::new(
 | 
					 | 
				
			||||||
                |ctx| pend_by_number(ctx as u16),
 | 
					 | 
				
			||||||
                irq.number() as *mut (),
 | 
					 | 
				
			||||||
            ))
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        unsafe { NVIC::unmask(irq) }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        executor.spawner().make_send()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -5,6 +5,6 @@
 | 
				
			|||||||
// This mod MUST go first, so that the others see its macros.
 | 
					// This mod MUST go first, so that the others see its macros.
 | 
				
			||||||
pub(crate) mod fmt;
 | 
					pub(crate) mod fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod executor;
 | 
					pub use embassy_executor as executor;
 | 
				
			||||||
pub mod interrupt;
 | 
					pub mod interrupt;
 | 
				
			||||||
pub mod peripheral;
 | 
					pub mod peripheral;
 | 
				
			||||||
 | 
				
			|||||||
@ -31,9 +31,22 @@ flavors = [
 | 
				
			|||||||
features = ["std", "nightly", "defmt"]
 | 
					features = ["std", "nightly", "defmt"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[features]
 | 
					[features]
 | 
				
			||||||
default = []
 | 
					
 | 
				
			||||||
std = ["critical-section/std"]
 | 
					# Architecture
 | 
				
			||||||
wasm = ["dep:wasm-bindgen", "dep:js-sys"]
 | 
					_arch = [] # some arch was picked
 | 
				
			||||||
 | 
					arch-std = ["_arch", "critical-section/std"]
 | 
				
			||||||
 | 
					arch-cortex-m = ["_arch", "dep:cortex-m"]
 | 
				
			||||||
 | 
					arch-xtensa = ["_arch"]
 | 
				
			||||||
 | 
					arch-riscv32 = ["_arch"]
 | 
				
			||||||
 | 
					arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Enable creating a `Pender` from an arbitrary function pointer callback.
 | 
				
			||||||
 | 
					pender-callback = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
 | 
				
			||||||
 | 
					executor-thread = []
 | 
				
			||||||
 | 
					# Enable the interrupt-mode executor (available in Cortex-M only)
 | 
				
			||||||
 | 
					executor-interrupt = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Enable nightly-only features
 | 
					# Enable nightly-only features
 | 
				
			||||||
nightly = []
 | 
					nightly = []
 | 
				
			||||||
@ -55,9 +68,11 @@ embassy-macros  = { version = "0.1.0", path = "../embassy-macros" }
 | 
				
			|||||||
embassy-time  = { version = "0.1.0", path = "../embassy-time", optional = true}
 | 
					embassy-time  = { version = "0.1.0", path = "../embassy-time", optional = true}
 | 
				
			||||||
atomic-polyfill = "1.0.1"
 | 
					atomic-polyfill = "1.0.1"
 | 
				
			||||||
critical-section = "1.1"
 | 
					critical-section = "1.1"
 | 
				
			||||||
cfg-if = "1.0.0"
 | 
					 | 
				
			||||||
static_cell = "1.0"
 | 
					static_cell = "1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# WASM dependencies
 | 
					# arch-cortex-m dependencies
 | 
				
			||||||
 | 
					cortex-m = { version = "0.7.6", optional = true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# arch-wasm dependencies
 | 
				
			||||||
wasm-bindgen = { version = "0.2.82", optional = true }
 | 
					wasm-bindgen = { version = "0.2.82", optional = true }
 | 
				
			||||||
js-sys = { version = "0.3", optional = true }
 | 
					js-sys = { version = "0.3", optional = true }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,59 +1,209 @@
 | 
				
			|||||||
use core::arch::asm;
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
use core::marker::PhantomData;
 | 
					pub use thread::*;
 | 
				
			||||||
use core::ptr;
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					mod thread {
 | 
				
			||||||
 | 
					    use core::arch::asm;
 | 
				
			||||||
 | 
					    use core::marker::PhantomData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{raw, Spawner};
 | 
					    #[cfg(feature = "nightly")]
 | 
				
			||||||
 | 
					    pub use embassy_macros::main_cortex_m as main;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Thread mode executor, using WFE/SEV.
 | 
					    use crate::raw::{Pender, PenderInner};
 | 
				
			||||||
///
 | 
					    use crate::{raw, Spawner};
 | 
				
			||||||
/// This is the simplest and most common kind of executor. It runs on
 | 
					 | 
				
			||||||
/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
 | 
					 | 
				
			||||||
/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
 | 
					 | 
				
			||||||
/// is executed, to make the `WFE` exit from sleep and poll the task.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// This executor allows for ultra low power consumption for chips where `WFE`
 | 
					 | 
				
			||||||
/// triggers low-power sleep without extra steps. If your chip requires extra steps,
 | 
					 | 
				
			||||||
/// you may use [`raw::Executor`] directly to program custom behavior.
 | 
					 | 
				
			||||||
pub struct Executor {
 | 
					 | 
				
			||||||
    inner: raw::Executor,
 | 
					 | 
				
			||||||
    not_send: PhantomData<*mut ()>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Executor {
 | 
					    #[derive(Copy, Clone)]
 | 
				
			||||||
    /// Create a new Executor.
 | 
					    pub(crate) struct ThreadPender;
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					
 | 
				
			||||||
        Self {
 | 
					    impl ThreadPender {
 | 
				
			||||||
            inner: raw::Executor::new(|_| unsafe { asm!("sev") }, ptr::null_mut()),
 | 
					        pub(crate) fn pend(self) {
 | 
				
			||||||
            not_send: PhantomData,
 | 
					            unsafe { core::arch::asm!("sev") }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Run the executor.
 | 
					    /// Thread mode executor, using WFE/SEV.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// The `init` closure is called with a [`Spawner`] that spawns tasks on
 | 
					    /// This is the simplest and most common kind of executor. It runs on
 | 
				
			||||||
    /// this executor. Use it to spawn the initial task(s). After `init` returns,
 | 
					    /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
 | 
				
			||||||
    /// the executor starts running the tasks.
 | 
					    /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
 | 
				
			||||||
 | 
					    /// is executed, to make the `WFE` exit from sleep and poll the task.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
 | 
					    /// This executor allows for ultra low power consumption for chips where `WFE`
 | 
				
			||||||
    /// for example by passing it as an argument to the initial tasks.
 | 
					    /// triggers low-power sleep without extra steps. If your chip requires extra steps,
 | 
				
			||||||
    ///
 | 
					    /// you may use [`raw::Executor`] directly to program custom behavior.
 | 
				
			||||||
    /// This function requires `&'static mut self`. This means you have to store the
 | 
					    pub struct Executor {
 | 
				
			||||||
    /// Executor instance in a place where it'll live forever and grants you mutable
 | 
					        inner: raw::Executor,
 | 
				
			||||||
    /// access. There's a few ways to do this:
 | 
					        not_send: PhantomData<*mut ()>,
 | 
				
			||||||
    ///
 | 
					    }
 | 
				
			||||||
    /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
 | 
					
 | 
				
			||||||
    /// - a `static mut` (unsafe)
 | 
					    impl Executor {
 | 
				
			||||||
    /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
 | 
					        /// Create a new Executor.
 | 
				
			||||||
    ///
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
    /// This function never returns.
 | 
					            Self {
 | 
				
			||||||
    pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
 | 
					                inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
 | 
				
			||||||
        init(self.inner.spawner());
 | 
					                not_send: PhantomData,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Run the executor.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// The `init` closure is called with a [`Spawner`] that spawns tasks on
 | 
				
			||||||
 | 
					        /// this executor. Use it to spawn the initial task(s). After `init` returns,
 | 
				
			||||||
 | 
					        /// the executor starts running the tasks.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
 | 
				
			||||||
 | 
					        /// for example by passing it as an argument to the initial tasks.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This function requires `&'static mut self`. This means you have to store the
 | 
				
			||||||
 | 
					        /// Executor instance in a place where it'll live forever and grants you mutable
 | 
				
			||||||
 | 
					        /// access. There's a few ways to do this:
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
 | 
				
			||||||
 | 
					        /// - a `static mut` (unsafe)
 | 
				
			||||||
 | 
					        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This function never returns.
 | 
				
			||||||
 | 
					        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
 | 
				
			||||||
 | 
					            init(self.inner.spawner());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            loop {
 | 
				
			||||||
 | 
					                unsafe {
 | 
				
			||||||
 | 
					                    self.inner.poll();
 | 
				
			||||||
 | 
					                    asm!("wfe");
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "executor-interrupt")]
 | 
				
			||||||
 | 
					pub use interrupt::*;
 | 
				
			||||||
 | 
					#[cfg(feature = "executor-interrupt")]
 | 
				
			||||||
 | 
					mod interrupt {
 | 
				
			||||||
 | 
					    use core::cell::UnsafeCell;
 | 
				
			||||||
 | 
					    use core::mem::MaybeUninit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use atomic_polyfill::{AtomicBool, Ordering};
 | 
				
			||||||
 | 
					    use cortex_m::interrupt::InterruptNumber;
 | 
				
			||||||
 | 
					    use cortex_m::peripheral::NVIC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use crate::raw::{self, Pender, PenderInner};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[derive(Clone, Copy)]
 | 
				
			||||||
 | 
					    pub(crate) struct InterruptPender(u16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl InterruptPender {
 | 
				
			||||||
 | 
					        pub(crate) fn pend(self) {
 | 
				
			||||||
 | 
					            // STIR is faster, but is only available in v7 and higher.
 | 
				
			||||||
 | 
					            #[cfg(not(armv6m))]
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) };
 | 
				
			||||||
 | 
					                nvic.request(self);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            #[cfg(armv6m)]
 | 
				
			||||||
 | 
					            cortex_m::peripheral::NVIC::pend(self);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender {
 | 
				
			||||||
 | 
					        fn number(self) -> u16 {
 | 
				
			||||||
 | 
					            self.0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Interrupt mode executor.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This executor runs tasks in interrupt mode. The interrupt handler is set up
 | 
				
			||||||
 | 
					    /// to poll tasks, and when a task is woken the interrupt is pended from software.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This allows running async tasks at a priority higher than thread mode. One
 | 
				
			||||||
 | 
					    /// use case is to leave thread mode free for non-async tasks. Another use case is
 | 
				
			||||||
 | 
					    /// to run multiple executors: one in thread mode for low priority tasks and another in
 | 
				
			||||||
 | 
					    /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
 | 
				
			||||||
 | 
					    /// priority ones.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// It is even possible to run multiple interrupt mode executors at different priorities,
 | 
				
			||||||
 | 
					    /// by assigning different priorities to the interrupts. For an example on how to do this,
 | 
				
			||||||
 | 
					    /// See the 'multiprio' example for 'embassy-nrf'.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// To use it, you have to pick an interrupt that won't be used by the hardware.
 | 
				
			||||||
 | 
					    /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
 | 
				
			||||||
 | 
					    /// If this is not the case, you may use an interrupt from any unused peripheral.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// It is somewhat more complex to use, it's recommended to use the thread-mode
 | 
				
			||||||
 | 
					    /// [`Executor`] instead, if it works for your use case.
 | 
				
			||||||
 | 
					    pub struct InterruptExecutor {
 | 
				
			||||||
 | 
					        started: AtomicBool,
 | 
				
			||||||
 | 
					        executor: UnsafeCell<MaybeUninit<raw::Executor>>,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsafe impl Send for InterruptExecutor {}
 | 
				
			||||||
 | 
					    unsafe impl Sync for InterruptExecutor {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl InterruptExecutor {
 | 
				
			||||||
 | 
					        /// Create a new, not started `InterruptExecutor`.
 | 
				
			||||||
 | 
					        #[inline]
 | 
				
			||||||
 | 
					        pub const fn new() -> Self {
 | 
				
			||||||
 | 
					            Self {
 | 
				
			||||||
 | 
					                started: AtomicBool::new(false),
 | 
				
			||||||
 | 
					                executor: UnsafeCell::new(MaybeUninit::uninit()),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Executor interrupt callback.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// # Safety
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// You MUST call this from the interrupt handler, and from nowhere else.
 | 
				
			||||||
 | 
					        pub unsafe fn on_interrupt(&'static self) {
 | 
				
			||||||
 | 
					            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
 | 
				
			||||||
 | 
					            executor.poll();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Start the executor.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This initializes the executor, enables the interrupt, and returns.
 | 
				
			||||||
 | 
					        /// The executor keeps running in the background through the interrupt.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
 | 
				
			||||||
 | 
					        /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a
 | 
				
			||||||
 | 
					        /// different "thread" (the interrupt), so spawning tasks on it is effectively
 | 
				
			||||||
 | 
					        /// sending them.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from
 | 
				
			||||||
 | 
					        /// a task running in it.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// # Interrupt requirements
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// You must set the interrupt priority before calling this method. You MUST NOT
 | 
				
			||||||
 | 
					        /// do it after.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {
 | 
				
			||||||
 | 
					            if self
 | 
				
			||||||
 | 
					                .started
 | 
				
			||||||
 | 
					                .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
 | 
				
			||||||
 | 
					                .is_err()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                panic!("InterruptExecutor::start() called multiple times on the same executor.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loop {
 | 
					 | 
				
			||||||
            unsafe {
 | 
					            unsafe {
 | 
				
			||||||
                self.inner.poll();
 | 
					                (&mut *self.executor.get())
 | 
				
			||||||
                asm!("wfe");
 | 
					                    .as_mut_ptr()
 | 
				
			||||||
            };
 | 
					                    .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender(
 | 
				
			||||||
 | 
					                        irq.number(),
 | 
				
			||||||
 | 
					                    )))))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            unsafe { NVIC::unmask(irq) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            executor.spawner().make_send()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,72 +1,83 @@
 | 
				
			|||||||
use core::marker::PhantomData;
 | 
					#[cfg(feature = "executor-interrupt")]
 | 
				
			||||||
use core::ptr;
 | 
					compile_error!("`executor-interrupt` is not supported with `arch-riscv32`.");
 | 
				
			||||||
use core::sync::atomic::{AtomicBool, Ordering};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{raw, Spawner};
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					pub use thread::*;
 | 
				
			||||||
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					mod thread {
 | 
				
			||||||
 | 
					    use core::marker::PhantomData;
 | 
				
			||||||
 | 
					    use core::sync::atomic::{AtomicBool, Ordering};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
 | 
					    use crate::raw::{Pender, PenderInner};
 | 
				
			||||||
///
 | 
					    use crate::{raw, Spawner};
 | 
				
			||||||
static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// RISCV32 Executor
 | 
					    #[derive(Copy, Clone)]
 | 
				
			||||||
pub struct Executor {
 | 
					    pub(crate) struct ThreadPender;
 | 
				
			||||||
    inner: raw::Executor,
 | 
					 | 
				
			||||||
    not_send: PhantomData<*mut ()>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Executor {
 | 
					    impl ThreadPender {
 | 
				
			||||||
    /// Create a new Executor.
 | 
					        #[allow(unused)]
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					        pub(crate) fn pend(self) {
 | 
				
			||||||
        Self {
 | 
					            SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
 | 
				
			||||||
            // use Signal_Work_Thread_Mode as substitute for local interrupt register
 | 
					 | 
				
			||||||
            inner: raw::Executor::new(
 | 
					 | 
				
			||||||
                |_| {
 | 
					 | 
				
			||||||
                    SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                ptr::null_mut(),
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            not_send: PhantomData,
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Run the executor.
 | 
					    /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
 | 
				
			||||||
    ///
 | 
					    static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
 | 
				
			||||||
    /// The `init` closure is called with a [`Spawner`] that spawns tasks on
 | 
					 | 
				
			||||||
    /// this executor. Use it to spawn the initial task(s). After `init` returns,
 | 
					 | 
				
			||||||
    /// the executor starts running the tasks.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
 | 
					 | 
				
			||||||
    /// for example by passing it as an argument to the initial tasks.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This function requires `&'static mut self`. This means you have to store the
 | 
					 | 
				
			||||||
    /// Executor instance in a place where it'll live forever and grants you mutable
 | 
					 | 
				
			||||||
    /// access. There's a few ways to do this:
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
 | 
					 | 
				
			||||||
    /// - a `static mut` (unsafe)
 | 
					 | 
				
			||||||
    /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This function never returns.
 | 
					 | 
				
			||||||
    pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
 | 
					 | 
				
			||||||
        init(self.inner.spawner());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loop {
 | 
					    /// RISCV32 Executor
 | 
				
			||||||
            unsafe {
 | 
					    pub struct Executor {
 | 
				
			||||||
                self.inner.poll();
 | 
					        inner: raw::Executor,
 | 
				
			||||||
                // we do not care about race conditions between the load and store operations, interrupts
 | 
					        not_send: PhantomData<*mut ()>,
 | 
				
			||||||
                //will only set this value to true.
 | 
					    }
 | 
				
			||||||
                critical_section::with(|_| {
 | 
					
 | 
				
			||||||
                    // if there is work to do, loop back to polling
 | 
					    impl Executor {
 | 
				
			||||||
                    // TODO can we relax this?
 | 
					        /// Create a new Executor.
 | 
				
			||||||
                    if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
                        SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
 | 
					            Self {
 | 
				
			||||||
                    }
 | 
					                inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
 | 
				
			||||||
                    // if not, wait for interrupt
 | 
					                not_send: PhantomData,
 | 
				
			||||||
                    else {
 | 
					            }
 | 
				
			||||||
                        core::arch::asm!("wfi");
 | 
					        }
 | 
				
			||||||
                    }
 | 
					
 | 
				
			||||||
                });
 | 
					        /// Run the executor.
 | 
				
			||||||
                // if an interrupt occurred while waiting, it will be serviced here
 | 
					        ///
 | 
				
			||||||
 | 
					        /// The `init` closure is called with a [`Spawner`] that spawns tasks on
 | 
				
			||||||
 | 
					        /// this executor. Use it to spawn the initial task(s). After `init` returns,
 | 
				
			||||||
 | 
					        /// the executor starts running the tasks.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
 | 
				
			||||||
 | 
					        /// for example by passing it as an argument to the initial tasks.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This function requires `&'static mut self`. This means you have to store the
 | 
				
			||||||
 | 
					        /// Executor instance in a place where it'll live forever and grants you mutable
 | 
				
			||||||
 | 
					        /// access. There's a few ways to do this:
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
 | 
				
			||||||
 | 
					        /// - a `static mut` (unsafe)
 | 
				
			||||||
 | 
					        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This function never returns.
 | 
				
			||||||
 | 
					        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
 | 
				
			||||||
 | 
					            init(self.inner.spawner());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            loop {
 | 
				
			||||||
 | 
					                unsafe {
 | 
				
			||||||
 | 
					                    self.inner.poll();
 | 
				
			||||||
 | 
					                    // we do not care about race conditions between the load and store operations, interrupts
 | 
				
			||||||
 | 
					                    //will only set this value to true.
 | 
				
			||||||
 | 
					                    critical_section::with(|_| {
 | 
				
			||||||
 | 
					                        // if there is work to do, loop back to polling
 | 
				
			||||||
 | 
					                        // TODO can we relax this?
 | 
				
			||||||
 | 
					                        if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
 | 
				
			||||||
 | 
					                            SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        // if not, wait for interrupt
 | 
				
			||||||
 | 
					                        else {
 | 
				
			||||||
 | 
					                            core::arch::asm!("wfi");
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    // if an interrupt occurred while waiting, it will be serviced here
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,84 +1,100 @@
 | 
				
			|||||||
use std::marker::PhantomData;
 | 
					#[cfg(feature = "executor-interrupt")]
 | 
				
			||||||
use std::sync::{Condvar, Mutex};
 | 
					compile_error!("`executor-interrupt` is not supported with `arch-std`.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{raw, Spawner};
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					pub use thread::*;
 | 
				
			||||||
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					mod thread {
 | 
				
			||||||
 | 
					    use std::marker::PhantomData;
 | 
				
			||||||
 | 
					    use std::sync::{Condvar, Mutex};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Single-threaded std-based executor.
 | 
					    #[cfg(feature = "nightly")]
 | 
				
			||||||
pub struct Executor {
 | 
					    pub use embassy_macros::main_std as main;
 | 
				
			||||||
    inner: raw::Executor,
 | 
					 | 
				
			||||||
    not_send: PhantomData<*mut ()>,
 | 
					 | 
				
			||||||
    signaler: &'static Signaler,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Executor {
 | 
					    use crate::raw::{Pender, PenderInner};
 | 
				
			||||||
    /// Create a new Executor.
 | 
					    use crate::{raw, Spawner};
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					
 | 
				
			||||||
        let signaler = &*Box::leak(Box::new(Signaler::new()));
 | 
					    #[derive(Copy, Clone)]
 | 
				
			||||||
        Self {
 | 
					    pub(crate) struct ThreadPender(&'static Signaler);
 | 
				
			||||||
            inner: raw::Executor::new(
 | 
					
 | 
				
			||||||
                |p| unsafe {
 | 
					    impl ThreadPender {
 | 
				
			||||||
                    let s = &*(p as *const () as *const Signaler);
 | 
					        #[allow(unused)]
 | 
				
			||||||
                    s.signal()
 | 
					        pub(crate) fn pend(self) {
 | 
				
			||||||
                },
 | 
					            self.0.signal()
 | 
				
			||||||
                signaler as *const _ as _,
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            not_send: PhantomData,
 | 
					 | 
				
			||||||
            signaler,
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Run the executor.
 | 
					    /// Single-threaded std-based executor.
 | 
				
			||||||
    ///
 | 
					    pub struct Executor {
 | 
				
			||||||
    /// The `init` closure is called with a [`Spawner`] that spawns tasks on
 | 
					        inner: raw::Executor,
 | 
				
			||||||
    /// this executor. Use it to spawn the initial task(s). After `init` returns,
 | 
					        not_send: PhantomData<*mut ()>,
 | 
				
			||||||
    /// the executor starts running the tasks.
 | 
					        signaler: &'static Signaler,
 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
 | 
					 | 
				
			||||||
    /// for example by passing it as an argument to the initial tasks.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This function requires `&'static mut self`. This means you have to store the
 | 
					 | 
				
			||||||
    /// Executor instance in a place where it'll live forever and grants you mutable
 | 
					 | 
				
			||||||
    /// access. There's a few ways to do this:
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
 | 
					 | 
				
			||||||
    /// - a `static mut` (unsafe)
 | 
					 | 
				
			||||||
    /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This function never returns.
 | 
					 | 
				
			||||||
    pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
 | 
					 | 
				
			||||||
        init(self.inner.spawner());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        loop {
 | 
					 | 
				
			||||||
            unsafe { self.inner.poll() };
 | 
					 | 
				
			||||||
            self.signaler.wait()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Signaler {
 | 
					    impl Executor {
 | 
				
			||||||
    mutex: Mutex<bool>,
 | 
					        /// Create a new Executor.
 | 
				
			||||||
    condvar: Condvar,
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
}
 | 
					            let signaler = &*Box::leak(Box::new(Signaler::new()));
 | 
				
			||||||
 | 
					            Self {
 | 
				
			||||||
 | 
					                inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))),
 | 
				
			||||||
 | 
					                not_send: PhantomData,
 | 
				
			||||||
 | 
					                signaler,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Signaler {
 | 
					        /// Run the executor.
 | 
				
			||||||
    fn new() -> Self {
 | 
					        ///
 | 
				
			||||||
        Self {
 | 
					        /// The `init` closure is called with a [`Spawner`] that spawns tasks on
 | 
				
			||||||
            mutex: Mutex::new(false),
 | 
					        /// this executor. Use it to spawn the initial task(s). After `init` returns,
 | 
				
			||||||
            condvar: Condvar::new(),
 | 
					        /// the executor starts running the tasks.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
 | 
				
			||||||
 | 
					        /// for example by passing it as an argument to the initial tasks.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This function requires `&'static mut self`. This means you have to store the
 | 
				
			||||||
 | 
					        /// Executor instance in a place where it'll live forever and grants you mutable
 | 
				
			||||||
 | 
					        /// access. There's a few ways to do this:
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
 | 
				
			||||||
 | 
					        /// - a `static mut` (unsafe)
 | 
				
			||||||
 | 
					        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This function never returns.
 | 
				
			||||||
 | 
					        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
 | 
				
			||||||
 | 
					            init(self.inner.spawner());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            loop {
 | 
				
			||||||
 | 
					                unsafe { self.inner.poll() };
 | 
				
			||||||
 | 
					                self.signaler.wait()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn wait(&self) {
 | 
					    struct Signaler {
 | 
				
			||||||
        let mut signaled = self.mutex.lock().unwrap();
 | 
					        mutex: Mutex<bool>,
 | 
				
			||||||
        while !*signaled {
 | 
					        condvar: Condvar,
 | 
				
			||||||
            signaled = self.condvar.wait(signaled).unwrap();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        *signaled = false;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn signal(&self) {
 | 
					    impl Signaler {
 | 
				
			||||||
        let mut signaled = self.mutex.lock().unwrap();
 | 
					        fn new() -> Self {
 | 
				
			||||||
        *signaled = true;
 | 
					            Self {
 | 
				
			||||||
        self.condvar.notify_one();
 | 
					                mutex: Mutex::new(false),
 | 
				
			||||||
 | 
					                condvar: Condvar::new(),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn wait(&self) {
 | 
				
			||||||
 | 
					            let mut signaled = self.mutex.lock().unwrap();
 | 
				
			||||||
 | 
					            while !*signaled {
 | 
				
			||||||
 | 
					                signaled = self.condvar.wait(signaled).unwrap();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            *signaled = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn signal(&self) {
 | 
				
			||||||
 | 
					            let mut signaled = self.mutex.lock().unwrap();
 | 
				
			||||||
 | 
					            *signaled = true;
 | 
				
			||||||
 | 
					            self.condvar.notify_one();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,74 +1,88 @@
 | 
				
			|||||||
use core::marker::PhantomData;
 | 
					#[cfg(feature = "executor-interrupt")]
 | 
				
			||||||
 | 
					compile_error!("`executor-interrupt` is not supported with `arch-wasm`.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use js_sys::Promise;
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
use wasm_bindgen::prelude::*;
 | 
					pub use thread::*;
 | 
				
			||||||
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					mod thread {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::raw::util::UninitCell;
 | 
					    use core::marker::PhantomData;
 | 
				
			||||||
use super::raw::{self};
 | 
					 | 
				
			||||||
use super::Spawner;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
 | 
					    #[cfg(feature = "nightly")]
 | 
				
			||||||
pub struct Executor {
 | 
					    pub use embassy_macros::main_wasm as main;
 | 
				
			||||||
    inner: raw::Executor,
 | 
					    use js_sys::Promise;
 | 
				
			||||||
    ctx: &'static WasmContext,
 | 
					    use wasm_bindgen::prelude::*;
 | 
				
			||||||
    not_send: PhantomData<*mut ()>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub(crate) struct WasmContext {
 | 
					    use crate::raw::util::UninitCell;
 | 
				
			||||||
    promise: Promise,
 | 
					    use crate::raw::{Pender, PenderInner};
 | 
				
			||||||
    closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
 | 
					    use crate::{raw, Spawner};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl WasmContext {
 | 
					    /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					    pub struct Executor {
 | 
				
			||||||
        Self {
 | 
					        inner: raw::Executor,
 | 
				
			||||||
            promise: Promise::resolve(&JsValue::undefined()),
 | 
					        ctx: &'static WasmContext,
 | 
				
			||||||
            closure: UninitCell::uninit(),
 | 
					        not_send: PhantomData<*mut ()>,
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Executor {
 | 
					    pub(crate) struct WasmContext {
 | 
				
			||||||
    /// Create a new Executor.
 | 
					        promise: Promise,
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					        closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
 | 
				
			||||||
        let ctx = &*Box::leak(Box::new(WasmContext::new()));
 | 
					    }
 | 
				
			||||||
        let inner = raw::Executor::new(
 | 
					
 | 
				
			||||||
            |p| unsafe {
 | 
					    #[derive(Copy, Clone)]
 | 
				
			||||||
                let ctx = &*(p as *const () as *const WasmContext);
 | 
					    pub(crate) struct ThreadPender(&'static WasmContext);
 | 
				
			||||||
                let _ = ctx.promise.then(ctx.closure.as_mut());
 | 
					
 | 
				
			||||||
            },
 | 
					    impl ThreadPender {
 | 
				
			||||||
            ctx as *const _ as _,
 | 
					        #[allow(unused)]
 | 
				
			||||||
        );
 | 
					        pub(crate) fn pend(self) {
 | 
				
			||||||
        Self {
 | 
					            let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() });
 | 
				
			||||||
            inner,
 | 
					 | 
				
			||||||
            not_send: PhantomData,
 | 
					 | 
				
			||||||
            ctx,
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Run the executor.
 | 
					    impl WasmContext {
 | 
				
			||||||
    ///
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
    /// The `init` closure is called with a [`Spawner`] that spawns tasks on
 | 
					            Self {
 | 
				
			||||||
    /// this executor. Use it to spawn the initial task(s). After `init` returns,
 | 
					                promise: Promise::resolve(&JsValue::undefined()),
 | 
				
			||||||
    /// the executor starts running the tasks.
 | 
					                closure: UninitCell::uninit(),
 | 
				
			||||||
    ///
 | 
					            }
 | 
				
			||||||
    /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
 | 
					        }
 | 
				
			||||||
    /// for example by passing it as an argument to the initial tasks.
 | 
					    }
 | 
				
			||||||
    ///
 | 
					
 | 
				
			||||||
    /// This function requires `&'static mut self`. This means you have to store the
 | 
					    impl Executor {
 | 
				
			||||||
    /// Executor instance in a place where it'll live forever and grants you mutable
 | 
					        /// Create a new Executor.
 | 
				
			||||||
    /// access. There's a few ways to do this:
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
    ///
 | 
					            let ctx = &*Box::leak(Box::new(WasmContext::new()));
 | 
				
			||||||
    /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
 | 
					            Self {
 | 
				
			||||||
    /// - a `static mut` (unsafe)
 | 
					                inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))),
 | 
				
			||||||
    /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
 | 
					                not_send: PhantomData,
 | 
				
			||||||
    pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
 | 
					                ctx,
 | 
				
			||||||
        unsafe {
 | 
					            }
 | 
				
			||||||
            let executor = &self.inner;
 | 
					        }
 | 
				
			||||||
            self.ctx.closure.write(Closure::new(move |_| {
 | 
					
 | 
				
			||||||
                executor.poll();
 | 
					        /// Run the executor.
 | 
				
			||||||
            }));
 | 
					        ///
 | 
				
			||||||
            init(self.inner.spawner());
 | 
					        /// The `init` closure is called with a [`Spawner`] that spawns tasks on
 | 
				
			||||||
 | 
					        /// this executor. Use it to spawn the initial task(s). After `init` returns,
 | 
				
			||||||
 | 
					        /// the executor starts running the tasks.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
 | 
				
			||||||
 | 
					        /// for example by passing it as an argument to the initial tasks.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This function requires `&'static mut self`. This means you have to store the
 | 
				
			||||||
 | 
					        /// Executor instance in a place where it'll live forever and grants you mutable
 | 
				
			||||||
 | 
					        /// access. There's a few ways to do this:
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
 | 
				
			||||||
 | 
					        /// - a `static mut` (unsafe)
 | 
				
			||||||
 | 
					        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
 | 
				
			||||||
 | 
					        pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
 | 
				
			||||||
 | 
					            unsafe {
 | 
				
			||||||
 | 
					                let executor = &self.inner;
 | 
				
			||||||
 | 
					                self.ctx.closure.write(Closure::new(move |_| {
 | 
				
			||||||
 | 
					                    executor.poll();
 | 
				
			||||||
 | 
					                }));
 | 
				
			||||||
 | 
					                init(self.inner.spawner());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,73 +1,84 @@
 | 
				
			|||||||
use core::marker::PhantomData;
 | 
					#[cfg(feature = "executor-interrupt")]
 | 
				
			||||||
use core::ptr;
 | 
					compile_error!("`executor-interrupt` is not supported with `arch-xtensa`.");
 | 
				
			||||||
use core::sync::atomic::{AtomicBool, Ordering};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{raw, Spawner};
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					pub use thread::*;
 | 
				
			||||||
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					mod thread {
 | 
				
			||||||
 | 
					    use core::marker::PhantomData;
 | 
				
			||||||
 | 
					    use core::sync::atomic::{AtomicBool, Ordering};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
 | 
					    use crate::raw::{Pender, PenderInner};
 | 
				
			||||||
///
 | 
					    use crate::{raw, Spawner};
 | 
				
			||||||
static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Xtensa Executor
 | 
					    #[derive(Copy, Clone)]
 | 
				
			||||||
pub struct Executor {
 | 
					    pub(crate) struct ThreadPender;
 | 
				
			||||||
    inner: raw::Executor,
 | 
					 | 
				
			||||||
    not_send: PhantomData<*mut ()>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Executor {
 | 
					    impl ThreadPender {
 | 
				
			||||||
    /// Create a new Executor.
 | 
					        #[allow(unused)]
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					        pub(crate) fn pend(self) {
 | 
				
			||||||
        Self {
 | 
					            SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
 | 
				
			||||||
            // use Signal_Work_Thread_Mode as substitute for local interrupt register
 | 
					 | 
				
			||||||
            inner: raw::Executor::new(
 | 
					 | 
				
			||||||
                |_| {
 | 
					 | 
				
			||||||
                    SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                ptr::null_mut(),
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            not_send: PhantomData,
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Run the executor.
 | 
					    /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
 | 
				
			||||||
    ///
 | 
					    static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
 | 
				
			||||||
    /// The `init` closure is called with a [`Spawner`] that spawns tasks on
 | 
					 | 
				
			||||||
    /// this executor. Use it to spawn the initial task(s). After `init` returns,
 | 
					 | 
				
			||||||
    /// the executor starts running the tasks.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
 | 
					 | 
				
			||||||
    /// for example by passing it as an argument to the initial tasks.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This function requires `&'static mut self`. This means you have to store the
 | 
					 | 
				
			||||||
    /// Executor instance in a place where it'll live forever and grants you mutable
 | 
					 | 
				
			||||||
    /// access. There's a few ways to do this:
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
 | 
					 | 
				
			||||||
    /// - a `static mut` (unsafe)
 | 
					 | 
				
			||||||
    /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This function never returns.
 | 
					 | 
				
			||||||
    pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
 | 
					 | 
				
			||||||
        init(self.inner.spawner());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loop {
 | 
					    /// Xtensa Executor
 | 
				
			||||||
            unsafe {
 | 
					    pub struct Executor {
 | 
				
			||||||
                self.inner.poll();
 | 
					        inner: raw::Executor,
 | 
				
			||||||
                // we do not care about race conditions between the load and store operations, interrupts
 | 
					        not_send: PhantomData<*mut ()>,
 | 
				
			||||||
                // will only set this value to true.
 | 
					    }
 | 
				
			||||||
                // if there is work to do, loop back to polling
 | 
					
 | 
				
			||||||
                // TODO can we relax this?
 | 
					    impl Executor {
 | 
				
			||||||
                critical_section::with(|_| {
 | 
					        /// Create a new Executor.
 | 
				
			||||||
                    if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
                        SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
 | 
					            Self {
 | 
				
			||||||
                    } else {
 | 
					                inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
 | 
				
			||||||
                        // waiti sets the PS.INTLEVEL when slipping into sleep
 | 
					                not_send: PhantomData,
 | 
				
			||||||
                        // because critical sections in Xtensa are implemented via increasing
 | 
					            }
 | 
				
			||||||
                        // PS.INTLEVEL the critical section ends here
 | 
					        }
 | 
				
			||||||
                        // take care not add code after `waiti` if it needs to be inside the CS
 | 
					
 | 
				
			||||||
                        core::arch::asm!("waiti 0"); // critical section ends here
 | 
					        /// Run the executor.
 | 
				
			||||||
                    }
 | 
					        ///
 | 
				
			||||||
                });
 | 
					        /// The `init` closure is called with a [`Spawner`] that spawns tasks on
 | 
				
			||||||
 | 
					        /// this executor. Use it to spawn the initial task(s). After `init` returns,
 | 
				
			||||||
 | 
					        /// the executor starts running the tasks.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
 | 
				
			||||||
 | 
					        /// for example by passing it as an argument to the initial tasks.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This function requires `&'static mut self`. This means you have to store the
 | 
				
			||||||
 | 
					        /// Executor instance in a place where it'll live forever and grants you mutable
 | 
				
			||||||
 | 
					        /// access. There's a few ways to do this:
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
 | 
				
			||||||
 | 
					        /// - a `static mut` (unsafe)
 | 
				
			||||||
 | 
					        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// This function never returns.
 | 
				
			||||||
 | 
					        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
 | 
				
			||||||
 | 
					            init(self.inner.spawner());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            loop {
 | 
				
			||||||
 | 
					                unsafe {
 | 
				
			||||||
 | 
					                    self.inner.poll();
 | 
				
			||||||
 | 
					                    // we do not care about race conditions between the load and store operations, interrupts
 | 
				
			||||||
 | 
					                    // will only set this value to true.
 | 
				
			||||||
 | 
					                    // if there is work to do, loop back to polling
 | 
				
			||||||
 | 
					                    // TODO can we relax this?
 | 
				
			||||||
 | 
					                    critical_section::with(|_| {
 | 
				
			||||||
 | 
					                        if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
 | 
				
			||||||
 | 
					                            SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            // waiti sets the PS.INTLEVEL when slipping into sleep
 | 
				
			||||||
 | 
					                            // because critical sections in Xtensa are implemented via increasing
 | 
				
			||||||
 | 
					                            // PS.INTLEVEL the critical section ends here
 | 
				
			||||||
 | 
					                            // take care not add code after `waiti` if it needs to be inside the CS
 | 
				
			||||||
 | 
					                            core::arch::asm!("waiti 0"); // critical section ends here
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)]
 | 
					#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)]
 | 
				
			||||||
#![cfg_attr(all(feature = "nightly", target_arch = "xtensa"), feature(asm_experimental_arch))]
 | 
					#![cfg_attr(all(feature = "nightly", feature = "arch-xtensa"), feature(asm_experimental_arch))]
 | 
				
			||||||
#![allow(clippy::new_without_default)]
 | 
					#![allow(clippy::new_without_default)]
 | 
				
			||||||
#![doc = include_str!("../README.md")]
 | 
					#![doc = include_str!("../README.md")]
 | 
				
			||||||
#![warn(missing_docs)]
 | 
					#![warn(missing_docs)]
 | 
				
			||||||
@ -10,47 +10,43 @@ pub(crate) mod fmt;
 | 
				
			|||||||
#[cfg(feature = "nightly")]
 | 
					#[cfg(feature = "nightly")]
 | 
				
			||||||
pub use embassy_macros::task;
 | 
					pub use embassy_macros::task;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cfg_if::cfg_if! {
 | 
					macro_rules! check_at_most_one {
 | 
				
			||||||
    if #[cfg(cortex_m)] {
 | 
					    (@amo [$($feats:literal)*] [] [$($res:tt)*]) => {
 | 
				
			||||||
        #[path="arch/cortex_m.rs"]
 | 
					        #[cfg(any($($res)*))]
 | 
				
			||||||
        mod arch;
 | 
					        compile_error!(concat!("At most one of these features can be enabled at the same time:", $(" `", $feats, "`",)*));
 | 
				
			||||||
        pub use arch::*;
 | 
					    };
 | 
				
			||||||
        #[cfg(feature = "nightly")]
 | 
					    (@amo $feats:tt [$curr:literal $($rest:literal)*] [$($res:tt)*]) => {
 | 
				
			||||||
        pub use embassy_macros::main_cortex_m as main;
 | 
					        check_at_most_one!(@amo $feats [$($rest)*] [$($res)* $(all(feature=$curr, feature=$rest),)*]);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    else if #[cfg(target_arch="riscv32")] {
 | 
					    ($($f:literal),*$(,)?) => {
 | 
				
			||||||
        #[path="arch/riscv32.rs"]
 | 
					        check_at_most_one!(@amo [$($f)*] [$($f)*] []);
 | 
				
			||||||
        mod arch;
 | 
					    };
 | 
				
			||||||
        pub use arch::*;
 | 
					 | 
				
			||||||
        #[cfg(feature = "nightly")]
 | 
					 | 
				
			||||||
        pub use embassy_macros::main_riscv as main;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] {
 | 
					 | 
				
			||||||
        #[path="arch/xtensa.rs"]
 | 
					 | 
				
			||||||
        mod arch;
 | 
					 | 
				
			||||||
        pub use arch::*;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else if #[cfg(feature="wasm")] {
 | 
					 | 
				
			||||||
        #[path="arch/wasm.rs"]
 | 
					 | 
				
			||||||
        mod arch;
 | 
					 | 
				
			||||||
        pub use arch::*;
 | 
					 | 
				
			||||||
        #[cfg(feature = "nightly")]
 | 
					 | 
				
			||||||
        pub use embassy_macros::main_wasm as main;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else if #[cfg(feature="std")] {
 | 
					 | 
				
			||||||
        #[path="arch/std.rs"]
 | 
					 | 
				
			||||||
        mod arch;
 | 
					 | 
				
			||||||
        pub use arch::*;
 | 
					 | 
				
			||||||
        #[cfg(feature = "nightly")]
 | 
					 | 
				
			||||||
        pub use embassy_macros::main_std as main;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-xtensa", "arch-std", "arch-wasm",);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "_arch")]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "arch-xtensa", path = "arch/xtensa.rs")]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "arch-std", path = "arch/std.rs")]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")]
 | 
				
			||||||
 | 
					mod arch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "_arch")]
 | 
				
			||||||
 | 
					pub use arch::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub mod raw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod spawner;
 | 
				
			||||||
 | 
					pub use spawner::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Implementation details for embassy macros.
 | 
				
			||||||
 | 
					/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
 | 
				
			||||||
#[doc(hidden)]
 | 
					#[doc(hidden)]
 | 
				
			||||||
/// Implementation details for embassy macros. DO NOT USE.
 | 
					pub mod _export {
 | 
				
			||||||
pub mod export {
 | 
					 | 
				
			||||||
    #[cfg(feature = "rtos-trace")]
 | 
					    #[cfg(feature = "rtos-trace")]
 | 
				
			||||||
    pub use rtos_trace::trace;
 | 
					    pub use rtos_trace::trace;
 | 
				
			||||||
 | 
					    pub use static_cell::StaticCell;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Expands the given block of code when `embassy-executor` is compiled with
 | 
					    /// Expands the given block of code when `embassy-executor` is compiled with
 | 
				
			||||||
    /// the `rtos-trace-interrupt` feature.
 | 
					    /// the `rtos-trace-interrupt` feature.
 | 
				
			||||||
@ -70,14 +66,3 @@ pub mod export {
 | 
				
			|||||||
        ($($tt:tt)*) => {};
 | 
					        ($($tt:tt)*) => {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
pub mod raw;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mod spawner;
 | 
					 | 
				
			||||||
pub use spawner::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
 | 
					 | 
				
			||||||
#[doc(hidden)]
 | 
					 | 
				
			||||||
pub mod _export {
 | 
					 | 
				
			||||||
    pub use static_cell::StaticCell;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,6 @@ use core::marker::PhantomData;
 | 
				
			|||||||
use core::mem;
 | 
					use core::mem;
 | 
				
			||||||
use core::pin::Pin;
 | 
					use core::pin::Pin;
 | 
				
			||||||
use core::ptr::NonNull;
 | 
					use core::ptr::NonNull;
 | 
				
			||||||
use core::sync::atomic::AtomicPtr;
 | 
					 | 
				
			||||||
use core::task::{Context, Poll};
 | 
					use core::task::{Context, Poll};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use atomic_polyfill::{AtomicU32, Ordering};
 | 
					use atomic_polyfill::{AtomicU32, Ordering};
 | 
				
			||||||
@ -290,10 +289,60 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy)]
 | 
				
			||||||
 | 
					pub(crate) enum PenderInner {
 | 
				
			||||||
 | 
					    #[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					    Thread(crate::arch::ThreadPender),
 | 
				
			||||||
 | 
					    #[cfg(feature = "executor-interrupt")]
 | 
				
			||||||
 | 
					    Interrupt(crate::arch::InterruptPender),
 | 
				
			||||||
 | 
					    #[cfg(feature = "pender-callback")]
 | 
				
			||||||
 | 
					    Callback { func: fn(*mut ()), context: *mut () },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsafe impl Send for PenderInner {}
 | 
				
			||||||
 | 
					unsafe impl Sync for PenderInner {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Platform/architecture-specific action executed when an executor has pending work.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// When a task within an executor is woken, the `Pender` is called. This does a
 | 
				
			||||||
 | 
					/// platform/architecture-specific action to signal there is pending work in the executor.
 | 
				
			||||||
 | 
					/// When this happens, you must arrange for [`Executor::poll`] to be called.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// You can think of it as a waker, but for the whole executor.
 | 
				
			||||||
 | 
					pub struct Pender(pub(crate) PenderInner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Pender {
 | 
				
			||||||
 | 
					    /// Create a `Pender` that will call an arbitrary function pointer.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Arguments
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// - `func`: The function pointer to call.
 | 
				
			||||||
 | 
					    /// - `context`: Opaque context pointer, that will be passed to the function pointer.
 | 
				
			||||||
 | 
					    #[cfg(feature = "pender-callback")]
 | 
				
			||||||
 | 
					    pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self {
 | 
				
			||||||
 | 
					        Self(PenderInner::Callback {
 | 
				
			||||||
 | 
					            func,
 | 
				
			||||||
 | 
					            context: context.into(),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Pender {
 | 
				
			||||||
 | 
					    pub(crate) fn pend(&self) {
 | 
				
			||||||
 | 
					        match self.0 {
 | 
				
			||||||
 | 
					            #[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					            PenderInner::Thread(x) => x.pend(),
 | 
				
			||||||
 | 
					            #[cfg(feature = "executor-interrupt")]
 | 
				
			||||||
 | 
					            PenderInner::Interrupt(x) => x.pend(),
 | 
				
			||||||
 | 
					            #[cfg(feature = "pender-callback")]
 | 
				
			||||||
 | 
					            PenderInner::Callback { func, context } => func(context),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub(crate) struct SyncExecutor {
 | 
					pub(crate) struct SyncExecutor {
 | 
				
			||||||
    run_queue: RunQueue,
 | 
					    run_queue: RunQueue,
 | 
				
			||||||
    signal_fn: fn(*mut ()),
 | 
					    pender: Pender,
 | 
				
			||||||
    signal_ctx: AtomicPtr<()>,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[cfg(feature = "integrated-timers")]
 | 
					    #[cfg(feature = "integrated-timers")]
 | 
				
			||||||
    pub(crate) timer_queue: timer_queue::TimerQueue,
 | 
					    pub(crate) timer_queue: timer_queue::TimerQueue,
 | 
				
			||||||
@ -302,16 +351,13 @@ pub(crate) struct SyncExecutor {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl SyncExecutor {
 | 
					impl SyncExecutor {
 | 
				
			||||||
    pub(crate) fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self {
 | 
					    pub(crate) fn new(pender: Pender) -> Self {
 | 
				
			||||||
        #[cfg(feature = "integrated-timers")]
 | 
					        #[cfg(feature = "integrated-timers")]
 | 
				
			||||||
        let alarm = unsafe { unwrap!(driver::allocate_alarm()) };
 | 
					        let alarm = unsafe { unwrap!(driver::allocate_alarm()) };
 | 
				
			||||||
        #[cfg(feature = "integrated-timers")]
 | 
					 | 
				
			||||||
        driver::set_alarm_callback(alarm, signal_fn, signal_ctx);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            run_queue: RunQueue::new(),
 | 
					            run_queue: RunQueue::new(),
 | 
				
			||||||
            signal_fn,
 | 
					            pender,
 | 
				
			||||||
            signal_ctx: AtomicPtr::new(signal_ctx),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #[cfg(feature = "integrated-timers")]
 | 
					            #[cfg(feature = "integrated-timers")]
 | 
				
			||||||
            timer_queue: timer_queue::TimerQueue::new(),
 | 
					            timer_queue: timer_queue::TimerQueue::new(),
 | 
				
			||||||
@ -332,10 +378,16 @@ impl SyncExecutor {
 | 
				
			|||||||
        trace::task_ready_begin(task.as_ptr() as u32);
 | 
					        trace::task_ready_begin(task.as_ptr() as u32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.run_queue.enqueue(cs, task) {
 | 
					        if self.run_queue.enqueue(cs, task) {
 | 
				
			||||||
            (self.signal_fn)(self.signal_ctx.load(Ordering::Relaxed))
 | 
					            self.pender.pend();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(feature = "integrated-timers")]
 | 
				
			||||||
 | 
					    fn alarm_callback(ctx: *mut ()) {
 | 
				
			||||||
 | 
					        let this: &Self = unsafe { &*(ctx as *const Self) };
 | 
				
			||||||
 | 
					        this.pender.pend();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(super) unsafe fn spawn(&'static self, task: TaskRef) {
 | 
					    pub(super) unsafe fn spawn(&'static self, task: TaskRef) {
 | 
				
			||||||
        task.header().executor.set(Some(self));
 | 
					        task.header().executor.set(Some(self));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -351,6 +403,9 @@ impl SyncExecutor {
 | 
				
			|||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
 | 
					    /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
 | 
				
			||||||
    pub(crate) unsafe fn poll(&'static self) {
 | 
					    pub(crate) unsafe fn poll(&'static self) {
 | 
				
			||||||
 | 
					        #[cfg(feature = "integrated-timers")]
 | 
				
			||||||
 | 
					        driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[allow(clippy::never_loop)]
 | 
					        #[allow(clippy::never_loop)]
 | 
				
			||||||
        loop {
 | 
					        loop {
 | 
				
			||||||
            #[cfg(feature = "integrated-timers")]
 | 
					            #[cfg(feature = "integrated-timers")]
 | 
				
			||||||
@ -417,14 +472,14 @@ impl SyncExecutor {
 | 
				
			|||||||
///
 | 
					///
 | 
				
			||||||
/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
 | 
					/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
 | 
				
			||||||
///   that "want to run").
 | 
					///   that "want to run").
 | 
				
			||||||
/// - You must supply a `signal_fn`. The executor will call it to notify you it has work
 | 
					/// - You must supply a [`Pender`]. The executor will call it to notify you it has work
 | 
				
			||||||
///   to do. You must arrange for `poll()` to be called as soon as possible.
 | 
					///   to do. You must arrange for `poll()` to be called as soon as possible.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// `signal_fn` can be called from *any* context: any thread, any interrupt priority
 | 
					/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority
 | 
				
			||||||
/// level, etc. It may be called synchronously from any `Executor` method call as well.
 | 
					/// level, etc. It may be called synchronously from any `Executor` method call as well.
 | 
				
			||||||
/// You must deal with this correctly.
 | 
					/// You must deal with this correctly.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates
 | 
					/// In particular, you must NOT call `poll` directly from the pender callback, as this violates
 | 
				
			||||||
/// the requirement for `poll` to not be called reentrantly.
 | 
					/// the requirement for `poll` to not be called reentrantly.
 | 
				
			||||||
#[repr(transparent)]
 | 
					#[repr(transparent)]
 | 
				
			||||||
pub struct Executor {
 | 
					pub struct Executor {
 | 
				
			||||||
@ -437,15 +492,15 @@ impl Executor {
 | 
				
			|||||||
    pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self {
 | 
					    pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self {
 | 
				
			||||||
        mem::transmute(inner)
 | 
					        mem::transmute(inner)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Create a new executor.
 | 
					    /// Create a new executor.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// When the executor has work to do, it will call `signal_fn` with
 | 
					    /// When the executor has work to do, it will call the [`Pender`].
 | 
				
			||||||
    /// `signal_ctx` as argument.
 | 
					 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// See [`Executor`] docs for details on `signal_fn`.
 | 
					    /// See [`Executor`] docs for details on `Pender`.
 | 
				
			||||||
    pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self {
 | 
					    pub fn new(pender: Pender) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            inner: SyncExecutor::new(signal_fn, signal_ctx),
 | 
					            inner: SyncExecutor::new(pender),
 | 
				
			||||||
            _not_sync: PhantomData,
 | 
					            _not_sync: PhantomData,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -468,16 +523,16 @@ impl Executor {
 | 
				
			|||||||
    /// This loops over all tasks that are queued to be polled (i.e. they're
 | 
					    /// This loops over all tasks that are queued to be polled (i.e. they're
 | 
				
			||||||
    /// freshly spawned or they've been woken). Other tasks are not polled.
 | 
					    /// freshly spawned or they've been woken). Other tasks are not polled.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// You must call `poll` after receiving a call to `signal_fn`. It is OK
 | 
					    /// You must call `poll` after receiving a call to the [`Pender`]. It is OK
 | 
				
			||||||
    /// to call `poll` even when not requested by `signal_fn`, but it wastes
 | 
					    /// to call `poll` even when not requested by the `Pender`, but it wastes
 | 
				
			||||||
    /// energy.
 | 
					    /// energy.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// # Safety
 | 
					    /// # Safety
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// You must NOT call `poll` reentrantly on the same executor.
 | 
					    /// You must NOT call `poll` reentrantly on the same executor.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you
 | 
					    /// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you
 | 
				
			||||||
    /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to
 | 
					    /// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to
 | 
				
			||||||
    /// somehow schedule for `poll()` to be called later, at a time you know for sure there's
 | 
					    /// somehow schedule for `poll()` to be called later, at a time you know for sure there's
 | 
				
			||||||
    /// no `poll()` already running.
 | 
					    /// no `poll()` already running.
 | 
				
			||||||
    pub unsafe fn poll(&'static self) {
 | 
					    pub unsafe fn poll(&'static self) {
 | 
				
			||||||
 | 
				
			|||||||
@ -10,12 +10,12 @@ pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> {
 | 
				
			|||||||
    let (isr_enter, isr_exit) = (
 | 
					    let (isr_enter, isr_exit) = (
 | 
				
			||||||
        quote! {
 | 
					        quote! {
 | 
				
			||||||
            ::embassy_executor::rtos_trace_interrupt! {
 | 
					            ::embassy_executor::rtos_trace_interrupt! {
 | 
				
			||||||
                ::embassy_executor::export::trace::isr_enter();
 | 
					                ::embassy_executor::_export::trace::isr_enter();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        quote! {
 | 
					        quote! {
 | 
				
			||||||
            ::embassy_executor::rtos_trace_interrupt! {
 | 
					            ::embassy_executor::rtos_trace_interrupt! {
 | 
				
			||||||
                ::embassy_executor::export::trace::isr_exit();
 | 
					                ::embassy_executor::_export::trace::isr_exit();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
				
			|||||||
@ -50,10 +50,13 @@ fn main() {
 | 
				
			|||||||
                // We *shouldn't* have singletons for these, but the HAL currently requires
 | 
					                // We *shouldn't* have singletons for these, but the HAL currently requires
 | 
				
			||||||
                // singletons, for using with RccPeripheral to enable/disable clocks to them.
 | 
					                // singletons, for using with RccPeripheral to enable/disable clocks to them.
 | 
				
			||||||
                "rcc" => {
 | 
					                "rcc" => {
 | 
				
			||||||
                    if r.version.starts_with("h7") {
 | 
					                    if r.version.starts_with("h7") || r.version.starts_with("f4") {
 | 
				
			||||||
                        singletons.push("MCO1".to_string());
 | 
					                        singletons.push("MCO1".to_string());
 | 
				
			||||||
                        singletons.push("MCO2".to_string());
 | 
					                        singletons.push("MCO2".to_string());
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                    if r.version.starts_with("l4") {
 | 
				
			||||||
 | 
					                        singletons.push("MCO".to_string());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    singletons.push(p.name.to_string());
 | 
					                    singletons.push(p.name.to_string());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                //"dbgmcu" => {}
 | 
					                //"dbgmcu" => {}
 | 
				
			||||||
@ -347,6 +350,7 @@ fn main() {
 | 
				
			|||||||
        (("i2c", "SCL"), quote!(crate::i2c::SclPin)),
 | 
					        (("i2c", "SCL"), quote!(crate::i2c::SclPin)),
 | 
				
			||||||
        (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)),
 | 
					        (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)),
 | 
				
			||||||
        (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)),
 | 
					        (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)),
 | 
				
			||||||
 | 
					        (("rcc", "MCO"), quote!(crate::rcc::McoPin)),
 | 
				
			||||||
        (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)),
 | 
					        (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)),
 | 
				
			||||||
        (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)),
 | 
					        (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)),
 | 
				
			||||||
        (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)),
 | 
					        (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)),
 | 
				
			||||||
@ -536,13 +540,22 @@ fn main() {
 | 
				
			|||||||
                    // MCO is special
 | 
					                    // MCO is special
 | 
				
			||||||
                    if pin.signal.starts_with("MCO_") {
 | 
					                    if pin.signal.starts_with("MCO_") {
 | 
				
			||||||
                        // Supported in H7 only for now
 | 
					                        // Supported in H7 only for now
 | 
				
			||||||
                        if regs.version.starts_with("h7") {
 | 
					                        if regs.version.starts_with("h7") || regs.version.starts_with("f4") {
 | 
				
			||||||
                            peri = format_ident!("{}", pin.signal.replace("_", ""));
 | 
					                            peri = format_ident!("{}", pin.signal.replace("_", ""));
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            continue;
 | 
					                            continue;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if pin.signal == "MCO" {
 | 
				
			||||||
 | 
					                        // Supported in H7 only for now
 | 
				
			||||||
 | 
					                        if regs.version.starts_with("l4") {
 | 
				
			||||||
 | 
					                            peri = format_ident!("MCO");
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    g.extend(quote! {
 | 
					                    g.extend(quote! {
 | 
				
			||||||
                        pin_trait_impl!(#tr, #peri, #pin_name, #af);
 | 
					                        pin_trait_impl!(#tr, #peri, #pin_name, #af);
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,16 @@
 | 
				
			|||||||
 | 
					use core::marker::PhantomData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use embassy_hal_common::into_ref;
 | 
				
			||||||
 | 
					use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::sealed::RccPeripheral;
 | 
					use super::sealed::RccPeripheral;
 | 
				
			||||||
 | 
					use crate::gpio::sealed::AFType;
 | 
				
			||||||
 | 
					use crate::gpio::Speed;
 | 
				
			||||||
use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
 | 
					use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
 | 
				
			||||||
use crate::pac::{FLASH, PWR, RCC};
 | 
					use crate::pac::{FLASH, PWR, RCC};
 | 
				
			||||||
use crate::rcc::{set_freqs, Clocks};
 | 
					use crate::rcc::{set_freqs, Clocks};
 | 
				
			||||||
use crate::time::Hertz;
 | 
					use crate::time::Hertz;
 | 
				
			||||||
 | 
					use crate::{peripherals, Peripheral};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// HSI speed
 | 
					/// HSI speed
 | 
				
			||||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
 | 
					pub const HSI_FREQ: Hertz = Hertz(16_000_000);
 | 
				
			||||||
@ -96,6 +104,164 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub enum McoClock {
 | 
				
			||||||
 | 
					    DIV1,
 | 
				
			||||||
 | 
					    DIV2,
 | 
				
			||||||
 | 
					    DIV3,
 | 
				
			||||||
 | 
					    DIV4,
 | 
				
			||||||
 | 
					    DIV5,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl McoClock {
 | 
				
			||||||
 | 
					    fn into_raw(&self) -> Mcopre {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            McoClock::DIV1 => Mcopre::DIV1,
 | 
				
			||||||
 | 
					            McoClock::DIV2 => Mcopre::DIV2,
 | 
				
			||||||
 | 
					            McoClock::DIV3 => Mcopre::DIV3,
 | 
				
			||||||
 | 
					            McoClock::DIV4 => Mcopre::DIV4,
 | 
				
			||||||
 | 
					            McoClock::DIV5 => Mcopre::DIV5,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					pub enum Mco1Source {
 | 
				
			||||||
 | 
					    Hsi,
 | 
				
			||||||
 | 
					    Lse,
 | 
				
			||||||
 | 
					    Hse,
 | 
				
			||||||
 | 
					    Pll,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for Mco1Source {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::Hsi
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait McoSource {
 | 
				
			||||||
 | 
					    type Raw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn into_raw(&self) -> Self::Raw;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl McoSource for Mco1Source {
 | 
				
			||||||
 | 
					    type Raw = Mco1;
 | 
				
			||||||
 | 
					    fn into_raw(&self) -> Self::Raw {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Mco1Source::Hsi => Mco1::HSI,
 | 
				
			||||||
 | 
					            Mco1Source::Lse => Mco1::LSE,
 | 
				
			||||||
 | 
					            Mco1Source::Hse => Mco1::HSE,
 | 
				
			||||||
 | 
					            Mco1Source::Pll => Mco1::PLL,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					pub enum Mco2Source {
 | 
				
			||||||
 | 
					    SysClk,
 | 
				
			||||||
 | 
					    Plli2s,
 | 
				
			||||||
 | 
					    Hse,
 | 
				
			||||||
 | 
					    Pll,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for Mco2Source {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::SysClk
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl McoSource for Mco2Source {
 | 
				
			||||||
 | 
					    type Raw = Mco2;
 | 
				
			||||||
 | 
					    fn into_raw(&self) -> Self::Raw {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Mco2Source::SysClk => Mco2::SYSCLK,
 | 
				
			||||||
 | 
					            Mco2Source::Plli2s => Mco2::PLLI2S,
 | 
				
			||||||
 | 
					            Mco2Source::Hse => Mco2::HSE,
 | 
				
			||||||
 | 
					            Mco2Source::Pll => Mco2::PLL,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub(crate) mod sealed {
 | 
				
			||||||
 | 
					    use stm32_metapac::rcc::vals::Mcopre;
 | 
				
			||||||
 | 
					    pub trait McoInstance {
 | 
				
			||||||
 | 
					        type Source;
 | 
				
			||||||
 | 
					        unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait McoInstance: sealed::McoInstance + 'static {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pin_trait!(McoPin, McoInstance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl sealed::McoInstance for peripherals::MCO1 {
 | 
				
			||||||
 | 
					    type Source = Mco1;
 | 
				
			||||||
 | 
					    unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
 | 
				
			||||||
 | 
					        RCC.cfgr().modify(|w| {
 | 
				
			||||||
 | 
					            w.set_mco1(source);
 | 
				
			||||||
 | 
					            w.set_mco1pre(prescaler);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        match source {
 | 
				
			||||||
 | 
					            Mco1::PLL => {
 | 
				
			||||||
 | 
					                RCC.cr().modify(|w| w.set_pllon(true));
 | 
				
			||||||
 | 
					                while !RCC.cr().read().pllrdy() {}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Mco1::HSI => {
 | 
				
			||||||
 | 
					                RCC.cr().modify(|w| w.set_hsion(true));
 | 
				
			||||||
 | 
					                while !RCC.cr().read().hsirdy() {}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					impl McoInstance for peripherals::MCO1 {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl sealed::McoInstance for peripherals::MCO2 {
 | 
				
			||||||
 | 
					    type Source = Mco2;
 | 
				
			||||||
 | 
					    unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
 | 
				
			||||||
 | 
					        RCC.cfgr().modify(|w| {
 | 
				
			||||||
 | 
					            w.set_mco2(source);
 | 
				
			||||||
 | 
					            w.set_mco2pre(prescaler);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        match source {
 | 
				
			||||||
 | 
					            Mco2::PLL => {
 | 
				
			||||||
 | 
					                RCC.cr().modify(|w| w.set_pllon(true));
 | 
				
			||||||
 | 
					                while !RCC.cr().read().pllrdy() {}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            #[cfg(not(stm32f410))]
 | 
				
			||||||
 | 
					            Mco2::PLLI2S => {
 | 
				
			||||||
 | 
					                RCC.cr().modify(|w| w.set_plli2son(true));
 | 
				
			||||||
 | 
					                while !RCC.cr().read().plli2srdy() {}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					impl McoInstance for peripherals::MCO2 {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Mco<'d, T: McoInstance> {
 | 
				
			||||||
 | 
					    phantom: PhantomData<&'d mut T>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'d, T: McoInstance> Mco<'d, T> {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        _peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        pin: impl Peripheral<P = impl McoPin<T>> + 'd,
 | 
				
			||||||
 | 
					        source: impl McoSource<Raw = T::Source>,
 | 
				
			||||||
 | 
					        prescaler: McoClock,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        into_ref!(pin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        critical_section::with(|_| unsafe {
 | 
				
			||||||
 | 
					            T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
 | 
				
			||||||
 | 
					            pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
 | 
				
			||||||
 | 
					            pin.set_speed(Speed::VeryHigh);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self { phantom: PhantomData }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsafe fn flash_setup(sysclk: u32) {
 | 
					unsafe fn flash_setup(sysclk: u32) {
 | 
				
			||||||
    use crate::pac::flash::vals::Latency;
 | 
					    use crate::pac::flash::vals::Latency;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,15 @@
 | 
				
			|||||||
 | 
					use core::marker::PhantomData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use embassy_hal_common::into_ref;
 | 
				
			||||||
 | 
					use stm32_metapac::rcc::vals::{Mcopre, Mcosel};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::gpio::sealed::AFType;
 | 
				
			||||||
 | 
					use crate::gpio::Speed;
 | 
				
			||||||
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
 | 
					use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
 | 
				
			||||||
use crate::pac::{FLASH, RCC};
 | 
					use crate::pac::{FLASH, RCC};
 | 
				
			||||||
use crate::rcc::{set_freqs, Clocks};
 | 
					use crate::rcc::{set_freqs, Clocks};
 | 
				
			||||||
use crate::time::Hertz;
 | 
					use crate::time::Hertz;
 | 
				
			||||||
 | 
					use crate::{peripherals, Peripheral};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// HSI speed
 | 
					/// HSI speed
 | 
				
			||||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
 | 
					pub const HSI_FREQ: Hertz = Hertz(16_000_000);
 | 
				
			||||||
@ -298,6 +306,131 @@ impl Default for Config {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub enum McoClock {
 | 
				
			||||||
 | 
					    DIV1,
 | 
				
			||||||
 | 
					    DIV2,
 | 
				
			||||||
 | 
					    DIV4,
 | 
				
			||||||
 | 
					    DIV8,
 | 
				
			||||||
 | 
					    DIV16,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl McoClock {
 | 
				
			||||||
 | 
					    fn into_raw(&self) -> Mcopre {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            McoClock::DIV1 => Mcopre::DIV1,
 | 
				
			||||||
 | 
					            McoClock::DIV2 => Mcopre::DIV2,
 | 
				
			||||||
 | 
					            McoClock::DIV4 => Mcopre::DIV4,
 | 
				
			||||||
 | 
					            McoClock::DIV8 => Mcopre::DIV8,
 | 
				
			||||||
 | 
					            McoClock::DIV16 => Mcopre::DIV16,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					pub enum Mco1Source {
 | 
				
			||||||
 | 
					    Disabled,
 | 
				
			||||||
 | 
					    Lse,
 | 
				
			||||||
 | 
					    Lsi,
 | 
				
			||||||
 | 
					    Hse,
 | 
				
			||||||
 | 
					    Hsi16,
 | 
				
			||||||
 | 
					    PllClk,
 | 
				
			||||||
 | 
					    SysClk,
 | 
				
			||||||
 | 
					    Msi,
 | 
				
			||||||
 | 
					    #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
 | 
				
			||||||
 | 
					    Hsi48,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for Mco1Source {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::Hsi16
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait McoSource {
 | 
				
			||||||
 | 
					    type Raw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn into_raw(&self) -> Self::Raw;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl McoSource for Mco1Source {
 | 
				
			||||||
 | 
					    type Raw = Mcosel;
 | 
				
			||||||
 | 
					    fn into_raw(&self) -> Self::Raw {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Mco1Source::Disabled => Mcosel::NOCLOCK,
 | 
				
			||||||
 | 
					            Mco1Source::Lse => Mcosel::LSE,
 | 
				
			||||||
 | 
					            Mco1Source::Lsi => Mcosel::LSI,
 | 
				
			||||||
 | 
					            Mco1Source::Hse => Mcosel::HSE,
 | 
				
			||||||
 | 
					            Mco1Source::Hsi16 => Mcosel::HSI16,
 | 
				
			||||||
 | 
					            Mco1Source::PllClk => Mcosel::PLL,
 | 
				
			||||||
 | 
					            Mco1Source::SysClk => Mcosel::SYSCLK,
 | 
				
			||||||
 | 
					            Mco1Source::Msi => Mcosel::MSI,
 | 
				
			||||||
 | 
					            #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
 | 
				
			||||||
 | 
					            Mco1Source::Hsi48 => Mcosel::HSI48,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub(crate) mod sealed {
 | 
				
			||||||
 | 
					    use stm32_metapac::rcc::vals::Mcopre;
 | 
				
			||||||
 | 
					    pub trait McoInstance {
 | 
				
			||||||
 | 
					        type Source;
 | 
				
			||||||
 | 
					        unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait McoInstance: sealed::McoInstance + 'static {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pin_trait!(McoPin, McoInstance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl sealed::McoInstance for peripherals::MCO {
 | 
				
			||||||
 | 
					    type Source = Mcosel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
 | 
				
			||||||
 | 
					        RCC.cfgr().modify(|w| {
 | 
				
			||||||
 | 
					            w.set_mcosel(source);
 | 
				
			||||||
 | 
					            w.set_mcopre(prescaler);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match source {
 | 
				
			||||||
 | 
					            Mcosel::HSI16 => {
 | 
				
			||||||
 | 
					                RCC.cr().modify(|w| w.set_hsion(true));
 | 
				
			||||||
 | 
					                while !RCC.cr().read().hsirdy() {}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
 | 
				
			||||||
 | 
					            Mcosel::HSI48 => {
 | 
				
			||||||
 | 
					                RCC.crrcr().modify(|w| w.set_hsi48on(true));
 | 
				
			||||||
 | 
					                while !RCC.crrcr().read().hsi48rdy() {}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl McoInstance for peripherals::MCO {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Mco<'d, T: McoInstance> {
 | 
				
			||||||
 | 
					    phantom: PhantomData<&'d mut T>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'d, T: McoInstance> Mco<'d, T> {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        _peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        pin: impl Peripheral<P = impl McoPin<T>> + 'd,
 | 
				
			||||||
 | 
					        source: impl McoSource<Raw = T::Source>,
 | 
				
			||||||
 | 
					        prescaler: McoClock,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        into_ref!(pin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        critical_section::with(|_| unsafe {
 | 
				
			||||||
 | 
					            T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
 | 
				
			||||||
 | 
					            pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
 | 
				
			||||||
 | 
					            pin.set_speed(Speed::VeryHigh);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self { phantom: PhantomData }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub(crate) unsafe fn init(config: Config) {
 | 
					pub(crate) unsafe fn init(config: Config) {
 | 
				
			||||||
    let (sys_clk, sw) = match config.mux {
 | 
					    let (sys_clk, sw) = match config.mux {
 | 
				
			||||||
        ClockSrc::MSI(range) => {
 | 
					        ClockSrc::MSI(range) => {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,55 +1,51 @@
 | 
				
			|||||||
use core::cell::RefCell;
 | 
					 | 
				
			||||||
use core::future::poll_fn;
 | 
					use core::future::poll_fn;
 | 
				
			||||||
use core::sync::atomic::{compiler_fence, Ordering};
 | 
					use core::slice;
 | 
				
			||||||
use core::task::Poll;
 | 
					use core::task::Poll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
 | 
					use embassy_cortex_m::interrupt::Interrupt;
 | 
				
			||||||
use embassy_hal_common::ring_buffer::RingBuffer;
 | 
					use embassy_hal_common::atomic_ring_buffer::RingBuffer;
 | 
				
			||||||
use embassy_sync::waitqueue::WakerRegistration;
 | 
					use embassy_sync::waitqueue::AtomicWaker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::*;
 | 
					use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct State<'d, T: BasicInstance>(StateStorage<StateInner<'d, T>>);
 | 
					pub struct State {
 | 
				
			||||||
impl<'d, T: BasicInstance> State<'d, T> {
 | 
					    rx_waker: AtomicWaker,
 | 
				
			||||||
 | 
					    rx_buf: RingBuffer,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tx_waker: AtomicWaker,
 | 
				
			||||||
 | 
					    tx_buf: RingBuffer,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl State {
 | 
				
			||||||
    pub const fn new() -> Self {
 | 
					    pub const fn new() -> Self {
 | 
				
			||||||
        Self(StateStorage::new())
 | 
					        Self {
 | 
				
			||||||
 | 
					            rx_buf: RingBuffer::new(),
 | 
				
			||||||
 | 
					            tx_buf: RingBuffer::new(),
 | 
				
			||||||
 | 
					            rx_waker: AtomicWaker::new(),
 | 
				
			||||||
 | 
					            tx_waker: AtomicWaker::new(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct StateInner<'d, T: BasicInstance> {
 | 
					 | 
				
			||||||
    phantom: PhantomData<&'d mut T>,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rx_waker: WakerRegistration,
 | 
					 | 
				
			||||||
    rx: RingBuffer<'d>,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    tx_waker: WakerRegistration,
 | 
					 | 
				
			||||||
    tx: RingBuffer<'d>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
unsafe impl<'d, T: BasicInstance> Send for StateInner<'d, T> {}
 | 
					 | 
				
			||||||
unsafe impl<'d, T: BasicInstance> Sync for StateInner<'d, T> {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub struct BufferedUart<'d, T: BasicInstance> {
 | 
					pub struct BufferedUart<'d, T: BasicInstance> {
 | 
				
			||||||
    inner: RefCell<PeripheralMutex<'d, StateInner<'d, T>>>,
 | 
					    rx: BufferedUartRx<'d, T>,
 | 
				
			||||||
 | 
					    tx: BufferedUartTx<'d, T>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct BufferedUartTx<'u, 'd, T: BasicInstance> {
 | 
					pub struct BufferedUartTx<'d, T: BasicInstance> {
 | 
				
			||||||
    inner: &'u BufferedUart<'d, T>,
 | 
					    phantom: PhantomData<&'d mut T>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct BufferedUartRx<'u, 'd, T: BasicInstance> {
 | 
					pub struct BufferedUartRx<'d, T: BasicInstance> {
 | 
				
			||||||
    inner: &'u BufferedUart<'d, T>,
 | 
					    phantom: PhantomData<&'d mut T>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'d, T: BasicInstance> BufferedUart<'d, T> {
 | 
					impl<'d, T: BasicInstance> BufferedUart<'d, T> {
 | 
				
			||||||
    pub fn new(
 | 
					    pub fn new(
 | 
				
			||||||
        state: &'d mut State<'d, T>,
 | 
					 | 
				
			||||||
        peri: impl Peripheral<P = T> + 'd,
 | 
					        peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        irq: impl Peripheral<P = T::Interrupt> + 'd,
 | 
				
			||||||
        rx: impl Peripheral<P = impl RxPin<T>> + 'd,
 | 
					        rx: impl Peripheral<P = impl RxPin<T>> + 'd,
 | 
				
			||||||
        tx: impl Peripheral<P = impl TxPin<T>> + 'd,
 | 
					        tx: impl Peripheral<P = impl TxPin<T>> + 'd,
 | 
				
			||||||
        irq: impl Peripheral<P = T::Interrupt> + 'd,
 | 
					 | 
				
			||||||
        tx_buffer: &'d mut [u8],
 | 
					        tx_buffer: &'d mut [u8],
 | 
				
			||||||
        rx_buffer: &'d mut [u8],
 | 
					        rx_buffer: &'d mut [u8],
 | 
				
			||||||
        config: Config,
 | 
					        config: Config,
 | 
				
			||||||
@ -57,15 +53,14 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
 | 
				
			|||||||
        T::enable();
 | 
					        T::enable();
 | 
				
			||||||
        T::reset();
 | 
					        T::reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config)
 | 
					        Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn new_with_rtscts(
 | 
					    pub fn new_with_rtscts(
 | 
				
			||||||
        state: &'d mut State<'d, T>,
 | 
					 | 
				
			||||||
        peri: impl Peripheral<P = T> + 'd,
 | 
					        peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        irq: impl Peripheral<P = T::Interrupt> + 'd,
 | 
				
			||||||
        rx: impl Peripheral<P = impl RxPin<T>> + 'd,
 | 
					        rx: impl Peripheral<P = impl RxPin<T>> + 'd,
 | 
				
			||||||
        tx: impl Peripheral<P = impl TxPin<T>> + 'd,
 | 
					        tx: impl Peripheral<P = impl TxPin<T>> + 'd,
 | 
				
			||||||
        irq: impl Peripheral<P = T::Interrupt> + 'd,
 | 
					 | 
				
			||||||
        rts: impl Peripheral<P = impl RtsPin<T>> + 'd,
 | 
					        rts: impl Peripheral<P = impl RtsPin<T>> + 'd,
 | 
				
			||||||
        cts: impl Peripheral<P = impl CtsPin<T>> + 'd,
 | 
					        cts: impl Peripheral<P = impl CtsPin<T>> + 'd,
 | 
				
			||||||
        tx_buffer: &'d mut [u8],
 | 
					        tx_buffer: &'d mut [u8],
 | 
				
			||||||
@ -86,16 +81,15 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config)
 | 
					        Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[cfg(not(usart_v1))]
 | 
					    #[cfg(not(usart_v1))]
 | 
				
			||||||
    pub fn new_with_de(
 | 
					    pub fn new_with_de(
 | 
				
			||||||
        state: &'d mut State<'d, T>,
 | 
					 | 
				
			||||||
        peri: impl Peripheral<P = T> + 'd,
 | 
					        peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        irq: impl Peripheral<P = T::Interrupt> + 'd,
 | 
				
			||||||
        rx: impl Peripheral<P = impl RxPin<T>> + 'd,
 | 
					        rx: impl Peripheral<P = impl RxPin<T>> + 'd,
 | 
				
			||||||
        tx: impl Peripheral<P = impl TxPin<T>> + 'd,
 | 
					        tx: impl Peripheral<P = impl TxPin<T>> + 'd,
 | 
				
			||||||
        irq: impl Peripheral<P = T::Interrupt> + 'd,
 | 
					 | 
				
			||||||
        de: impl Peripheral<P = impl DePin<T>> + 'd,
 | 
					        de: impl Peripheral<P = impl DePin<T>> + 'd,
 | 
				
			||||||
        tx_buffer: &'d mut [u8],
 | 
					        tx_buffer: &'d mut [u8],
 | 
				
			||||||
        rx_buffer: &'d mut [u8],
 | 
					        rx_buffer: &'d mut [u8],
 | 
				
			||||||
@ -113,23 +107,27 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config)
 | 
					        Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn new_inner(
 | 
					    fn new_inner(
 | 
				
			||||||
        state: &'d mut State<'d, T>,
 | 
					 | 
				
			||||||
        _peri: impl Peripheral<P = T> + 'd,
 | 
					        _peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        irq: impl Peripheral<P = T::Interrupt> + 'd,
 | 
				
			||||||
        rx: impl Peripheral<P = impl RxPin<T>> + 'd,
 | 
					        rx: impl Peripheral<P = impl RxPin<T>> + 'd,
 | 
				
			||||||
        tx: impl Peripheral<P = impl TxPin<T>> + 'd,
 | 
					        tx: impl Peripheral<P = impl TxPin<T>> + 'd,
 | 
				
			||||||
        irq: impl Peripheral<P = T::Interrupt> + 'd,
 | 
					 | 
				
			||||||
        tx_buffer: &'d mut [u8],
 | 
					        tx_buffer: &'d mut [u8],
 | 
				
			||||||
        rx_buffer: &'d mut [u8],
 | 
					        rx_buffer: &'d mut [u8],
 | 
				
			||||||
        config: Config,
 | 
					        config: Config,
 | 
				
			||||||
    ) -> BufferedUart<'d, T> {
 | 
					    ) -> BufferedUart<'d, T> {
 | 
				
			||||||
        into_ref!(_peri, rx, tx, irq);
 | 
					        into_ref!(_peri, rx, tx, irq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let r = T::regs();
 | 
					        let state = T::buffered_state();
 | 
				
			||||||
 | 
					        let len = tx_buffer.len();
 | 
				
			||||||
 | 
					        unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
 | 
				
			||||||
 | 
					        let len = rx_buffer.len();
 | 
				
			||||||
 | 
					        unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let r = T::regs();
 | 
				
			||||||
        unsafe {
 | 
					        unsafe {
 | 
				
			||||||
            rx.set_as_af(rx.af_num(), AFType::Input);
 | 
					            rx.set_as_af(rx.af_num(), AFType::Input);
 | 
				
			||||||
            tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
 | 
					            tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
 | 
				
			||||||
@ -147,273 +145,259 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        irq.set_handler(on_interrupt::<T>);
 | 
				
			||||||
 | 
					        irq.unpend();
 | 
				
			||||||
 | 
					        irq.enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner {
 | 
					            rx: BufferedUartRx { phantom: PhantomData },
 | 
				
			||||||
                phantom: PhantomData,
 | 
					            tx: BufferedUartTx { phantom: PhantomData },
 | 
				
			||||||
                tx: RingBuffer::new(tx_buffer),
 | 
					 | 
				
			||||||
                tx_waker: WakerRegistration::new(),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                rx: RingBuffer::new(rx_buffer),
 | 
					 | 
				
			||||||
                rx_waker: WakerRegistration::new(),
 | 
					 | 
				
			||||||
            })),
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn split<'u>(&'u mut self) -> (BufferedUartRx<'u, 'd, T>, BufferedUartTx<'u, 'd, T>) {
 | 
					    pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) {
 | 
				
			||||||
        (BufferedUartRx { inner: self }, BufferedUartTx { inner: self })
 | 
					        (self.tx, self.rx)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result<usize, Error> {
 | 
					impl<'d, T: BasicInstance> BufferedUartRx<'d, T> {
 | 
				
			||||||
 | 
					    async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
 | 
				
			||||||
        poll_fn(move |cx| {
 | 
					        poll_fn(move |cx| {
 | 
				
			||||||
            let mut do_pend = false;
 | 
					            let state = T::buffered_state();
 | 
				
			||||||
            let mut inner = self.inner.borrow_mut();
 | 
					            let mut rx_reader = unsafe { state.rx_buf.reader() };
 | 
				
			||||||
            let res = inner.with(|state| {
 | 
					            let data = rx_reader.pop_slice();
 | 
				
			||||||
                compiler_fence(Ordering::SeqCst);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // We have data ready in buffer? Return it.
 | 
					            if !data.is_empty() {
 | 
				
			||||||
                let data = state.rx.pop_buf();
 | 
					                let len = data.len().min(buf.len());
 | 
				
			||||||
                if !data.is_empty() {
 | 
					                buf[..len].copy_from_slice(&data[..len]);
 | 
				
			||||||
                    let len = data.len().min(buf.len());
 | 
					 | 
				
			||||||
                    buf[..len].copy_from_slice(&data[..len]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if state.rx.is_full() {
 | 
					                let do_pend = state.rx_buf.is_full();
 | 
				
			||||||
                        do_pend = true;
 | 
					                rx_reader.pop_done(len);
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    state.rx.pop(len);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return Poll::Ready(Ok(len));
 | 
					                if do_pend {
 | 
				
			||||||
 | 
					                    unsafe { T::Interrupt::steal().pend() };
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return Poll::Ready(Ok(len));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            state.rx_waker.register(cx.waker());
 | 
				
			||||||
 | 
					            Poll::Pending
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn blocking_read(&self, buf: &mut [u8]) -> Result<usize, Error> {
 | 
				
			||||||
 | 
					        loop {
 | 
				
			||||||
 | 
					            let state = T::buffered_state();
 | 
				
			||||||
 | 
					            let mut rx_reader = unsafe { state.rx_buf.reader() };
 | 
				
			||||||
 | 
					            let data = rx_reader.pop_slice();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if !data.is_empty() {
 | 
				
			||||||
 | 
					                let len = data.len().min(buf.len());
 | 
				
			||||||
 | 
					                buf[..len].copy_from_slice(&data[..len]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let do_pend = state.rx_buf.is_full();
 | 
				
			||||||
 | 
					                rx_reader.pop_done(len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if do_pend {
 | 
				
			||||||
 | 
					                    unsafe { T::Interrupt::steal().pend() };
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return Ok(len);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn fill_buf(&self) -> Result<&[u8], Error> {
 | 
				
			||||||
 | 
					        poll_fn(move |cx| {
 | 
				
			||||||
 | 
					            let state = T::buffered_state();
 | 
				
			||||||
 | 
					            let mut rx_reader = unsafe { state.rx_buf.reader() };
 | 
				
			||||||
 | 
					            let (p, n) = rx_reader.pop_buf();
 | 
				
			||||||
 | 
					            if n == 0 {
 | 
				
			||||||
                state.rx_waker.register(cx.waker());
 | 
					                state.rx_waker.register(cx.waker());
 | 
				
			||||||
                Poll::Pending
 | 
					                return Poll::Pending;
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if do_pend {
 | 
					 | 
				
			||||||
                inner.pend();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            res
 | 
					            let buf = unsafe { slice::from_raw_parts(p, n) };
 | 
				
			||||||
 | 
					            Poll::Ready(Ok(buf))
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn inner_blocking_read(&self, buf: &mut [u8]) -> Result<usize, Error> {
 | 
					    fn consume(&self, amt: usize) {
 | 
				
			||||||
        loop {
 | 
					        let state = T::buffered_state();
 | 
				
			||||||
            let mut do_pend = false;
 | 
					        let mut rx_reader = unsafe { state.rx_buf.reader() };
 | 
				
			||||||
            let mut inner = self.inner.borrow_mut();
 | 
					        let full = state.rx_buf.is_full();
 | 
				
			||||||
            let n = inner.with(|state| {
 | 
					        rx_reader.pop_done(amt);
 | 
				
			||||||
                compiler_fence(Ordering::SeqCst);
 | 
					        if full {
 | 
				
			||||||
 | 
					            unsafe { T::Interrupt::steal().pend() };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // We have data ready in buffer? Return it.
 | 
					impl<'d, T: BasicInstance> BufferedUartTx<'d, T> {
 | 
				
			||||||
                let data = state.rx.pop_buf();
 | 
					    async fn write(&self, buf: &[u8]) -> Result<usize, Error> {
 | 
				
			||||||
                if !data.is_empty() {
 | 
					        poll_fn(move |cx| {
 | 
				
			||||||
                    let len = data.len().min(buf.len());
 | 
					            let state = T::buffered_state();
 | 
				
			||||||
                    buf[..len].copy_from_slice(&data[..len]);
 | 
					            let empty = state.tx_buf.is_empty();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if state.rx.is_full() {
 | 
					            let mut tx_writer = unsafe { state.tx_buf.writer() };
 | 
				
			||||||
                        do_pend = true;
 | 
					            let data = tx_writer.push_slice();
 | 
				
			||||||
                    }
 | 
					            if data.is_empty() {
 | 
				
			||||||
                    state.rx.pop(len);
 | 
					                state.tx_waker.register(cx.waker());
 | 
				
			||||||
 | 
					                return Poll::Pending;
 | 
				
			||||||
                    return len;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                0
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if do_pend {
 | 
					 | 
				
			||||||
                inner.pend();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if n > 0 {
 | 
					            let n = data.len().min(buf.len());
 | 
				
			||||||
 | 
					            data[..n].copy_from_slice(&buf[..n]);
 | 
				
			||||||
 | 
					            tx_writer.push_done(n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if empty {
 | 
				
			||||||
 | 
					                unsafe { T::Interrupt::steal() }.pend();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Poll::Ready(Ok(n))
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn flush(&self) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        poll_fn(move |cx| {
 | 
				
			||||||
 | 
					            let state = T::buffered_state();
 | 
				
			||||||
 | 
					            if !state.tx_buf.is_empty() {
 | 
				
			||||||
 | 
					                state.tx_waker.register(cx.waker());
 | 
				
			||||||
 | 
					                return Poll::Pending;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Poll::Ready(Ok(()))
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn blocking_write(&self, buf: &[u8]) -> Result<usize, Error> {
 | 
				
			||||||
 | 
					        loop {
 | 
				
			||||||
 | 
					            let state = T::buffered_state();
 | 
				
			||||||
 | 
					            let empty = state.tx_buf.is_empty();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let mut tx_writer = unsafe { state.tx_buf.writer() };
 | 
				
			||||||
 | 
					            let data = tx_writer.push_slice();
 | 
				
			||||||
 | 
					            if !data.is_empty() {
 | 
				
			||||||
 | 
					                let n = data.len().min(buf.len());
 | 
				
			||||||
 | 
					                data[..n].copy_from_slice(&buf[..n]);
 | 
				
			||||||
 | 
					                tx_writer.push_done(n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if empty {
 | 
				
			||||||
 | 
					                    unsafe { T::Interrupt::steal() }.pend();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return Ok(n);
 | 
					                return Ok(n);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, Error> {
 | 
					    fn blocking_flush(&self) -> Result<(), Error> {
 | 
				
			||||||
        poll_fn(move |cx| {
 | 
					 | 
				
			||||||
            let mut inner = self.inner.borrow_mut();
 | 
					 | 
				
			||||||
            let (poll, empty) = inner.with(|state| {
 | 
					 | 
				
			||||||
                let empty = state.tx.is_empty();
 | 
					 | 
				
			||||||
                let tx_buf = state.tx.push_buf();
 | 
					 | 
				
			||||||
                if tx_buf.is_empty() {
 | 
					 | 
				
			||||||
                    state.tx_waker.register(cx.waker());
 | 
					 | 
				
			||||||
                    return (Poll::Pending, empty);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let n = core::cmp::min(tx_buf.len(), buf.len());
 | 
					 | 
				
			||||||
                tx_buf[..n].copy_from_slice(&buf[..n]);
 | 
					 | 
				
			||||||
                state.tx.push(n);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                (Poll::Ready(Ok(n)), empty)
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            if empty {
 | 
					 | 
				
			||||||
                inner.pend();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            poll
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .await
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async fn inner_flush<'a>(&'a self) -> Result<(), Error> {
 | 
					 | 
				
			||||||
        poll_fn(move |cx| {
 | 
					 | 
				
			||||||
            self.inner.borrow_mut().with(|state| {
 | 
					 | 
				
			||||||
                if !state.tx.is_empty() {
 | 
					 | 
				
			||||||
                    state.tx_waker.register(cx.waker());
 | 
					 | 
				
			||||||
                    return Poll::Pending;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                Poll::Ready(Ok(()))
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .await
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn inner_blocking_write(&self, buf: &[u8]) -> Result<usize, Error> {
 | 
					 | 
				
			||||||
        loop {
 | 
					        loop {
 | 
				
			||||||
            let mut inner = self.inner.borrow_mut();
 | 
					            let state = T::buffered_state();
 | 
				
			||||||
            let (n, empty) = inner.with(|state| {
 | 
					            if state.tx_buf.is_empty() {
 | 
				
			||||||
                let empty = state.tx.is_empty();
 | 
					 | 
				
			||||||
                let tx_buf = state.tx.push_buf();
 | 
					 | 
				
			||||||
                if tx_buf.is_empty() {
 | 
					 | 
				
			||||||
                    return (0, empty);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let n = core::cmp::min(tx_buf.len(), buf.len());
 | 
					 | 
				
			||||||
                tx_buf[..n].copy_from_slice(&buf[..n]);
 | 
					 | 
				
			||||||
                state.tx.push(n);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                (n, empty)
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            if empty {
 | 
					 | 
				
			||||||
                inner.pend();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if n != 0 {
 | 
					 | 
				
			||||||
                return Ok(n);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn inner_blocking_flush(&self) -> Result<(), Error> {
 | 
					 | 
				
			||||||
        loop {
 | 
					 | 
				
			||||||
            if !self.inner.borrow_mut().with(|state| state.tx.is_empty()) {
 | 
					 | 
				
			||||||
                return Ok(());
 | 
					                return Ok(());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> {
 | 
					 | 
				
			||||||
        poll_fn(move |cx| {
 | 
					 | 
				
			||||||
            self.inner.borrow_mut().with(|state| {
 | 
					 | 
				
			||||||
                compiler_fence(Ordering::SeqCst);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // We have data ready in buffer? Return it.
 | 
					 | 
				
			||||||
                let buf = state.rx.pop_buf();
 | 
					 | 
				
			||||||
                if !buf.is_empty() {
 | 
					 | 
				
			||||||
                    let buf: &[u8] = buf;
 | 
					 | 
				
			||||||
                    // Safety: buffer lives as long as uart
 | 
					 | 
				
			||||||
                    let buf: &[u8] = unsafe { core::mem::transmute(buf) };
 | 
					 | 
				
			||||||
                    return Poll::Ready(Ok(buf));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                state.rx_waker.register(cx.waker());
 | 
					 | 
				
			||||||
                Poll::<Result<&[u8], Error>>::Pending
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .await
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn inner_consume(&self, amt: usize) {
 | 
					 | 
				
			||||||
        let mut inner = self.inner.borrow_mut();
 | 
					 | 
				
			||||||
        let signal = inner.with(|state| {
 | 
					 | 
				
			||||||
            let full = state.rx.is_full();
 | 
					 | 
				
			||||||
            state.rx.pop(amt);
 | 
					 | 
				
			||||||
            full
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        if signal {
 | 
					 | 
				
			||||||
            inner.pend();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: BasicInstance> StateInner<'d, T>
 | 
					impl<'d, T: BasicInstance> Drop for BufferedUartRx<'d, T> {
 | 
				
			||||||
where
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
    Self: 'd,
 | 
					        let state = T::buffered_state();
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    fn on_rx(&mut self) {
 | 
					 | 
				
			||||||
        let r = T::regs();
 | 
					 | 
				
			||||||
        unsafe {
 | 
					        unsafe {
 | 
				
			||||||
            let sr = sr(r).read();
 | 
					            state.rx_buf.deinit();
 | 
				
			||||||
            clear_interrupt_flags(r, sr);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // This read also clears the error and idle interrupt flags on v1.
 | 
					            // TX is inactive if the the buffer is not available.
 | 
				
			||||||
            let b = rdr(r).read_volatile();
 | 
					            // We can now unregister the interrupt handler
 | 
				
			||||||
 | 
					            if state.tx_buf.len() == 0 {
 | 
				
			||||||
            if sr.rxne() {
 | 
					                T::Interrupt::steal().disable();
 | 
				
			||||||
                if sr.pe() {
 | 
					 | 
				
			||||||
                    warn!("Parity error");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if sr.fe() {
 | 
					 | 
				
			||||||
                    warn!("Framing error");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if sr.ne() {
 | 
					 | 
				
			||||||
                    warn!("Noise error");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if sr.ore() {
 | 
					 | 
				
			||||||
                    warn!("Overrun error");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let buf = self.rx.push_buf();
 | 
					 | 
				
			||||||
                if !buf.is_empty() {
 | 
					 | 
				
			||||||
                    buf[0] = b;
 | 
					 | 
				
			||||||
                    self.rx.push(1);
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    warn!("RX buffer full, discard received byte");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if self.rx.is_full() {
 | 
					 | 
				
			||||||
                    self.rx_waker.wake();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if sr.idle() {
 | 
					 | 
				
			||||||
                self.rx_waker.wake();
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_tx(&mut self) {
 | 
					 | 
				
			||||||
        let r = T::regs();
 | 
					 | 
				
			||||||
        unsafe {
 | 
					 | 
				
			||||||
            if sr(r).read().txe() {
 | 
					 | 
				
			||||||
                let buf = self.tx.pop_buf();
 | 
					 | 
				
			||||||
                if !buf.is_empty() {
 | 
					 | 
				
			||||||
                    r.cr1().modify(|w| {
 | 
					 | 
				
			||||||
                        w.set_txeie(true);
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                    tdr(r).write_volatile(buf[0].into());
 | 
					 | 
				
			||||||
                    self.tx.pop(1);
 | 
					 | 
				
			||||||
                    self.tx_waker.wake();
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    // Disable interrupt until we have something to transmit again
 | 
					 | 
				
			||||||
                    r.cr1().modify(|w| {
 | 
					 | 
				
			||||||
                        w.set_txeie(false);
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: BasicInstance> PeripheralState for StateInner<'d, T>
 | 
					impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> {
 | 
				
			||||||
where
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
    Self: 'd,
 | 
					        let state = T::buffered_state();
 | 
				
			||||||
{
 | 
					        unsafe {
 | 
				
			||||||
    type Interrupt = T::Interrupt;
 | 
					            state.tx_buf.deinit();
 | 
				
			||||||
    fn on_interrupt(&mut self) {
 | 
					
 | 
				
			||||||
        self.on_rx();
 | 
					            // RX is inactive if the the buffer is not available.
 | 
				
			||||||
        self.on_tx();
 | 
					            // We can now unregister the interrupt handler
 | 
				
			||||||
 | 
					            if state.rx_buf.len() == 0 {
 | 
				
			||||||
 | 
					                T::Interrupt::steal().disable();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsafe fn on_interrupt<T: BasicInstance>(_: *mut ()) {
 | 
				
			||||||
 | 
					    let r = T::regs();
 | 
				
			||||||
 | 
					    let state = T::buffered_state();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // RX
 | 
				
			||||||
 | 
					    unsafe {
 | 
				
			||||||
 | 
					        let sr = sr(r).read();
 | 
				
			||||||
 | 
					        clear_interrupt_flags(r, sr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if sr.rxne() {
 | 
				
			||||||
 | 
					            if sr.pe() {
 | 
				
			||||||
 | 
					                warn!("Parity error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if sr.fe() {
 | 
				
			||||||
 | 
					                warn!("Framing error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if sr.ne() {
 | 
				
			||||||
 | 
					                warn!("Noise error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if sr.ore() {
 | 
				
			||||||
 | 
					                warn!("Overrun error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let mut rx_writer = state.rx_buf.writer();
 | 
				
			||||||
 | 
					            let buf = rx_writer.push_slice();
 | 
				
			||||||
 | 
					            if !buf.is_empty() {
 | 
				
			||||||
 | 
					                // This read also clears the error and idle interrupt flags on v1.
 | 
				
			||||||
 | 
					                buf[0] = rdr(r).read_volatile();
 | 
				
			||||||
 | 
					                rx_writer.push_done(1);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // FIXME: Should we disable any further RX interrupts when the buffer becomes full.
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if state.rx_buf.is_full() {
 | 
				
			||||||
 | 
					                state.rx_waker.wake();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if sr.idle() {
 | 
				
			||||||
 | 
					            state.rx_waker.wake();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TX
 | 
				
			||||||
 | 
					    unsafe {
 | 
				
			||||||
 | 
					        if sr(r).read().txe() {
 | 
				
			||||||
 | 
					            let mut tx_reader = state.tx_buf.reader();
 | 
				
			||||||
 | 
					            let buf = tx_reader.pop_slice();
 | 
				
			||||||
 | 
					            if !buf.is_empty() {
 | 
				
			||||||
 | 
					                r.cr1().modify(|w| {
 | 
				
			||||||
 | 
					                    w.set_txeie(true);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                tdr(r).write_volatile(buf[0].into());
 | 
				
			||||||
 | 
					                tx_reader.pop_done(1);
 | 
				
			||||||
 | 
					                state.tx_waker.wake();
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Disable interrupt until we have something to transmit again
 | 
				
			||||||
 | 
					                r.cr1().modify(|w| {
 | 
				
			||||||
 | 
					                    w.set_txeie(false);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -427,94 +411,284 @@ impl<'d, T: BasicInstance> embedded_io::Io for BufferedUart<'d, T> {
 | 
				
			|||||||
    type Error = Error;
 | 
					    type Error = Error;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartRx<'u, 'd, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartRx<'d, T> {
 | 
				
			||||||
    type Error = Error;
 | 
					    type Error = Error;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartTx<'d, T> {
 | 
				
			||||||
    type Error = Error;
 | 
					    type Error = Error;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> {
 | 
				
			||||||
    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
					    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
				
			||||||
        self.inner_read(buf).await
 | 
					        self.rx.read(buf).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'d, T> {
 | 
				
			||||||
    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
					    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
				
			||||||
        self.inner.inner_read(buf).await
 | 
					        Self::read(self, buf).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> {
 | 
				
			||||||
    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
 | 
					    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
 | 
				
			||||||
        self.inner_fill_buf().await
 | 
					        self.rx.fill_buf().await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn consume(&mut self, amt: usize) {
 | 
					    fn consume(&mut self, amt: usize) {
 | 
				
			||||||
        self.inner_consume(amt)
 | 
					        self.rx.consume(amt)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> {
 | 
				
			||||||
    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
 | 
					    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
 | 
				
			||||||
        self.inner.inner_fill_buf().await
 | 
					        Self::fill_buf(self).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn consume(&mut self, amt: usize) {
 | 
					    fn consume(&mut self, amt: usize) {
 | 
				
			||||||
        self.inner.inner_consume(amt)
 | 
					        Self::consume(self, amt)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> {
 | 
				
			||||||
    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
					    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
				
			||||||
        self.inner_write(buf).await
 | 
					        self.tx.write(buf).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
					    async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
        self.inner_flush().await
 | 
					        self.tx.flush().await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'d, T> {
 | 
				
			||||||
    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
					    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
				
			||||||
        self.inner.inner_write(buf).await
 | 
					        Self::write(self, buf).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
					    async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
        self.inner.inner_flush().await
 | 
					        Self::flush(self).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> {
 | 
				
			||||||
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
					    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
				
			||||||
        self.inner_blocking_read(buf)
 | 
					        self.rx.blocking_read(buf)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'u, 'd, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'d, T> {
 | 
				
			||||||
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
					    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
				
			||||||
        self.inner.inner_blocking_read(buf)
 | 
					        self.blocking_read(buf)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> {
 | 
				
			||||||
    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
					    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
				
			||||||
        self.inner_blocking_write(buf)
 | 
					        self.tx.blocking_write(buf)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn flush(&mut self) -> Result<(), Self::Error> {
 | 
					    fn flush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
        self.inner_blocking_flush()
 | 
					        self.tx.blocking_flush()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'u, 'd, T> {
 | 
					impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'d, T> {
 | 
				
			||||||
    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
					    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
				
			||||||
        self.inner.inner_blocking_write(buf)
 | 
					        Self::blocking_write(self, buf)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn flush(&mut self) -> Result<(), Self::Error> {
 | 
					    fn flush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
        self.inner.inner_blocking_flush()
 | 
					        Self::blocking_flush(self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod eh02 {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_02::serial::Read<u8> for BufferedUartRx<'d, T> {
 | 
				
			||||||
 | 
					        type Error = Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
 | 
				
			||||||
 | 
					            let r = T::regs();
 | 
				
			||||||
 | 
					            unsafe {
 | 
				
			||||||
 | 
					                let sr = sr(r).read();
 | 
				
			||||||
 | 
					                if sr.pe() {
 | 
				
			||||||
 | 
					                    rdr(r).read_volatile();
 | 
				
			||||||
 | 
					                    Err(nb::Error::Other(Error::Parity))
 | 
				
			||||||
 | 
					                } else if sr.fe() {
 | 
				
			||||||
 | 
					                    rdr(r).read_volatile();
 | 
				
			||||||
 | 
					                    Err(nb::Error::Other(Error::Framing))
 | 
				
			||||||
 | 
					                } else if sr.ne() {
 | 
				
			||||||
 | 
					                    rdr(r).read_volatile();
 | 
				
			||||||
 | 
					                    Err(nb::Error::Other(Error::Noise))
 | 
				
			||||||
 | 
					                } else if sr.ore() {
 | 
				
			||||||
 | 
					                    rdr(r).read_volatile();
 | 
				
			||||||
 | 
					                    Err(nb::Error::Other(Error::Overrun))
 | 
				
			||||||
 | 
					                } else if sr.rxne() {
 | 
				
			||||||
 | 
					                    Ok(rdr(r).read_volatile())
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Err(nb::Error::WouldBlock)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUartTx<'d, T> {
 | 
				
			||||||
 | 
					        type Error = Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            while !buffer.is_empty() {
 | 
				
			||||||
 | 
					                match self.blocking_write(buffer) {
 | 
				
			||||||
 | 
					                    Ok(0) => panic!("zero-length write."),
 | 
				
			||||||
 | 
					                    Ok(n) => buffer = &buffer[n..],
 | 
				
			||||||
 | 
					                    Err(e) => return Err(e),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Ok(())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn bflush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.blocking_flush()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_02::serial::Read<u8> for BufferedUart<'d, T> {
 | 
				
			||||||
 | 
					        type Error = Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
 | 
				
			||||||
 | 
					            embedded_hal_02::serial::Read::read(&mut self.rx)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUart<'d, T> {
 | 
				
			||||||
 | 
					        type Error = Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            while !buffer.is_empty() {
 | 
				
			||||||
 | 
					                match self.tx.blocking_write(buffer) {
 | 
				
			||||||
 | 
					                    Ok(0) => panic!("zero-length write."),
 | 
				
			||||||
 | 
					                    Ok(n) => buffer = &buffer[n..],
 | 
				
			||||||
 | 
					                    Err(e) => return Err(e),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Ok(())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn bflush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.tx.blocking_flush()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "unstable-traits")]
 | 
				
			||||||
 | 
					mod eh1 {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> {
 | 
				
			||||||
 | 
					        type Error = Error;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> {
 | 
				
			||||||
 | 
					        type Error = Error;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> {
 | 
				
			||||||
 | 
					        type Error = Error;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_nb::serial::Read for BufferedUartRx<'d, T> {
 | 
				
			||||||
 | 
					        fn read(&mut self) -> nb::Result<u8, Self::Error> {
 | 
				
			||||||
 | 
					            embedded_hal_02::serial::Read::read(self)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> {
 | 
				
			||||||
 | 
					        fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.blocking_write(buffer).map(drop)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn flush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.blocking_flush()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> {
 | 
				
			||||||
 | 
					        fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn flush(&mut self) -> nb::Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.blocking_flush().map_err(nb::Error::Other)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_nb::serial::Read for BufferedUart<'d, T> {
 | 
				
			||||||
 | 
					        fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
 | 
				
			||||||
 | 
					            embedded_hal_02::serial::Read::read(&mut self.rx)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUart<'d, T> {
 | 
				
			||||||
 | 
					        fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.tx.blocking_write(buffer).map(drop)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn flush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.tx.blocking_flush()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> {
 | 
				
			||||||
 | 
					        fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn flush(&mut self) -> nb::Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.tx.blocking_flush().map_err(nb::Error::Other)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(all(
 | 
				
			||||||
 | 
					    feature = "unstable-traits",
 | 
				
			||||||
 | 
					    feature = "nightly",
 | 
				
			||||||
 | 
					    feature = "_todo_embedded_hal_serial"
 | 
				
			||||||
 | 
					))]
 | 
				
			||||||
 | 
					mod eha {
 | 
				
			||||||
 | 
					    use core::future::Future;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_async::serial::Write for BufferedUartTx<'d, T> {
 | 
				
			||||||
 | 
					        async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            Self::write(buf)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            Self::flush()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_async::serial::Read for BufferedUartRx<'d, T> {
 | 
				
			||||||
 | 
					        async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            Self::read(buf)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_async::serial::Write for BufferedUart<'d, T> {
 | 
				
			||||||
 | 
					        async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.tx.write(buf)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.tx.flush()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'d, T: BasicInstance> embedded_hal_async::serial::Read for BufferedUart<'d, T> {
 | 
				
			||||||
 | 
					        async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					            self.rx.read(buf)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1112,6 +1112,9 @@ pub(crate) mod sealed {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        fn regs() -> Regs;
 | 
					        fn regs() -> Regs;
 | 
				
			||||||
        fn state() -> &'static State;
 | 
					        fn state() -> &'static State;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "nightly")]
 | 
				
			||||||
 | 
					        fn buffered_state() -> &'static buffered::State;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub trait FullInstance: BasicInstance {
 | 
					    pub trait FullInstance: BasicInstance {
 | 
				
			||||||
@ -1147,6 +1150,12 @@ macro_rules! impl_lpuart {
 | 
				
			|||||||
                static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new();
 | 
					                static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new();
 | 
				
			||||||
                &STATE
 | 
					                &STATE
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            #[cfg(feature = "nightly")]
 | 
				
			||||||
 | 
					            fn buffered_state() -> &'static buffered::State {
 | 
				
			||||||
 | 
					                static STATE: buffered::State = buffered::State::new();
 | 
				
			||||||
 | 
					                &STATE
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        impl BasicInstance for peripherals::$inst {}
 | 
					        impl BasicInstance for peripherals::$inst {}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
 | 
					embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] }
 | 
				
			||||||
embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
 | 
					embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
 | 
				
			||||||
embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" }
 | 
					embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
 | 
					embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] }
 | 
				
			||||||
embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] }
 | 
					embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] }
 | 
				
			||||||
embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp" }
 | 
					embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp" }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"]  }
 | 
				
			||||||
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
					embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"]  }
 | 
				
			||||||
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
					embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
 | 
					embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"]  }
 | 
				
			||||||
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
					embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"]  }
 | 
				
			||||||
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
					embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"]  }
 | 
				
			||||||
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
					embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"]  }
 | 
				
			||||||
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
					embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"]  }
 | 
				
			||||||
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
					embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@ log = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync" }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync" }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features=["rtos-trace", "rtos-trace-interrupt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time" }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time" }
 | 
				
			||||||
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
 | 
					embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,7 @@ nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/night
 | 
				
			|||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 | 
					embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
 | 
				
			||||||
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
 | 
					embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
 | 
				
			||||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
 | 
					embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 | 
				
			|||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [
 | 
				
			||||||
    "defmt",
 | 
					    "defmt",
 | 
				
			||||||
] }
 | 
					] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = [
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", 
 | 
				
			||||||
    "nightly",
 | 
					    "nightly",
 | 
				
			||||||
    "defmt",
 | 
					    "defmt",
 | 
				
			||||||
    "integrated-timers",
 | 
					    "integrated-timers",
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
 | 
				
			||||||
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] }
 | 
					embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] }
 | 
				
			||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
					embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] }
 | 
				
			||||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] }
 | 
					embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] }
 | 
				
			||||||
embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" }
 | 
					embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"]  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ defmt = "0.3"
 | 
				
			|||||||
defmt-rtt = "0.4"
 | 
					defmt-rtt = "0.4"
 | 
				
			||||||
panic-probe = "0.3"
 | 
					panic-probe = "0.3"
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] }
 | 
				
			||||||
static_cell = "1.0"
 | 
					static_cell = "1.0"
 | 
				
			||||||
 | 
				
			|||||||
@ -62,7 +62,7 @@ use core::mem;
 | 
				
			|||||||
use cortex_m::peripheral::NVIC;
 | 
					use cortex_m::peripheral::NVIC;
 | 
				
			||||||
use cortex_m_rt::entry;
 | 
					use cortex_m_rt::entry;
 | 
				
			||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_stm32::executor::{Executor, InterruptExecutor};
 | 
					use embassy_executor::{Executor, InterruptExecutor};
 | 
				
			||||||
use embassy_stm32::interrupt;
 | 
					use embassy_stm32::interrupt;
 | 
				
			||||||
use embassy_stm32::pac::Interrupt;
 | 
					use embassy_stm32::pac::Interrupt;
 | 
				
			||||||
use embassy_time::{Duration, Instant, Timer};
 | 
					use embassy_time::{Duration, Instant, Timer};
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"]  }
 | 
				
			||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
					embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"]  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"]  }
 | 
				
			||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
					embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
				
			||||||
 | 
				
			|||||||
@ -62,7 +62,7 @@ use core::mem;
 | 
				
			|||||||
use cortex_m::peripheral::NVIC;
 | 
					use cortex_m::peripheral::NVIC;
 | 
				
			||||||
use cortex_m_rt::entry;
 | 
					use cortex_m_rt::entry;
 | 
				
			||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_stm32::executor::{Executor, InterruptExecutor};
 | 
					use embassy_executor::{Executor, InterruptExecutor};
 | 
				
			||||||
use embassy_stm32::interrupt;
 | 
					use embassy_stm32::interrupt;
 | 
				
			||||||
use embassy_stm32::pac::Interrupt;
 | 
					use embassy_stm32::pac::Interrupt;
 | 
				
			||||||
use embassy_time::{Duration, Instant, Timer};
 | 
					use embassy_time::{Duration, Instant, Timer};
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"]  }
 | 
				
			||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
					embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										30
									
								
								examples/stm32f4/src/bin/mco.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								examples/stm32f4/src/bin/mco.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					#![no_std]
 | 
				
			||||||
 | 
					#![no_main]
 | 
				
			||||||
 | 
					#![feature(type_alias_impl_trait)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use defmt::*;
 | 
				
			||||||
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
 | 
					use embassy_stm32::gpio::{Level, Output, Speed};
 | 
				
			||||||
 | 
					use embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoClock};
 | 
				
			||||||
 | 
					use embassy_time::{Duration, Timer};
 | 
				
			||||||
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[embassy_executor::main]
 | 
				
			||||||
 | 
					async fn main(_spawner: Spawner) {
 | 
				
			||||||
 | 
					    let p = embassy_stm32::init(Default::default());
 | 
				
			||||||
 | 
					    info!("Hello World!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::DIV1);
 | 
				
			||||||
 | 
					    let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::Pll, McoClock::DIV4);
 | 
				
			||||||
 | 
					    let mut led = Output::new(p.PB7, Level::High, Speed::Low);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loop {
 | 
				
			||||||
 | 
					        info!("high");
 | 
				
			||||||
 | 
					        led.set_high();
 | 
				
			||||||
 | 
					        Timer::after(Duration::from_millis(300)).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        info!("low");
 | 
				
			||||||
 | 
					        led.set_low();
 | 
				
			||||||
 | 
					        Timer::after(Duration::from_millis(300)).await;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -62,7 +62,7 @@ use core::mem;
 | 
				
			|||||||
use cortex_m::peripheral::NVIC;
 | 
					use cortex_m::peripheral::NVIC;
 | 
				
			||||||
use cortex_m_rt::entry;
 | 
					use cortex_m_rt::entry;
 | 
				
			||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_stm32::executor::{Executor, InterruptExecutor};
 | 
					use embassy_executor::{Executor, InterruptExecutor};
 | 
				
			||||||
use embassy_stm32::interrupt;
 | 
					use embassy_stm32::interrupt;
 | 
				
			||||||
use embassy_stm32::pac::Interrupt;
 | 
					use embassy_stm32::pac::Interrupt;
 | 
				
			||||||
use embassy_time::{Duration, Instant, Timer};
 | 
					use embassy_time::{Duration, Instant, Timer};
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@
 | 
				
			|||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
use embassy_stm32::interrupt;
 | 
					use embassy_stm32::interrupt;
 | 
				
			||||||
use embassy_stm32::usart::{BufferedUart, Config, State};
 | 
					use embassy_stm32::usart::{BufferedUart, Config};
 | 
				
			||||||
use embedded_io::asynch::BufRead;
 | 
					use embedded_io::asynch::BufRead;
 | 
				
			||||||
use {defmt_rtt as _, panic_probe as _};
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -16,20 +16,10 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let config = Config::default();
 | 
					    let config = Config::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut state = State::new();
 | 
					 | 
				
			||||||
    let irq = interrupt::take!(USART3);
 | 
					    let irq = interrupt::take!(USART3);
 | 
				
			||||||
    let mut tx_buf = [0u8; 32];
 | 
					    let mut tx_buf = [0u8; 32];
 | 
				
			||||||
    let mut rx_buf = [0u8; 32];
 | 
					    let mut rx_buf = [0u8; 32];
 | 
				
			||||||
    let mut buf_usart = BufferedUart::new(
 | 
					    let mut buf_usart = BufferedUart::new(p.USART3, irq, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config);
 | 
				
			||||||
        &mut state,
 | 
					 | 
				
			||||||
        p.USART3,
 | 
					 | 
				
			||||||
        p.PD9,
 | 
					 | 
				
			||||||
        p.PD8,
 | 
					 | 
				
			||||||
        irq,
 | 
					 | 
				
			||||||
        &mut tx_buf,
 | 
					 | 
				
			||||||
        &mut rx_buf,
 | 
					 | 
				
			||||||
        config,
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loop {
 | 
					    loop {
 | 
				
			||||||
        let buf = buf_usart.fill_buf().await.unwrap();
 | 
					        let buf = buf_usart.fill_buf().await.unwrap();
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"]  }
 | 
				
			||||||
embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
 | 
					embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"]  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"]  }
 | 
				
			||||||
embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" }
 | 
					embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
 | 
				
			||||||
embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] }
 | 
					embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] }
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan",
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"]  }
 | 
				
			||||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true}
 | 
					embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@
 | 
				
			|||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
use embassy_stm32::interrupt;
 | 
					use embassy_stm32::interrupt;
 | 
				
			||||||
use embassy_stm32::usart::{BufferedUart, Config, State};
 | 
					use embassy_stm32::usart::{BufferedUart, Config};
 | 
				
			||||||
use embedded_io::asynch::{Read, Write};
 | 
					use embedded_io::asynch::{Read, Write};
 | 
				
			||||||
use {defmt_rtt as _, panic_probe as _};
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,20 +20,8 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
    let mut config = Config::default();
 | 
					    let mut config = Config::default();
 | 
				
			||||||
    config.baudrate = 9600;
 | 
					    config.baudrate = 9600;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut state = State::new();
 | 
					 | 
				
			||||||
    let irq = interrupt::take!(USART2);
 | 
					    let irq = interrupt::take!(USART2);
 | 
				
			||||||
    let mut usart = unsafe {
 | 
					    let mut usart = unsafe { BufferedUart::new(p.USART2, irq, p.PA3, p.PA2, &mut TX_BUFFER, &mut RX_BUFFER, config) };
 | 
				
			||||||
        BufferedUart::new(
 | 
					 | 
				
			||||||
            &mut state,
 | 
					 | 
				
			||||||
            p.USART2,
 | 
					 | 
				
			||||||
            p.PA3,
 | 
					 | 
				
			||||||
            p.PA2,
 | 
					 | 
				
			||||||
            irq,
 | 
					 | 
				
			||||||
            &mut TX_BUFFER,
 | 
					 | 
				
			||||||
            &mut RX_BUFFER,
 | 
					 | 
				
			||||||
            config,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    usart.write_all(b"Hello Embassy World!\r\n").await.unwrap();
 | 
					    usart.write_all(b"Hello Embassy World!\r\n").await.unwrap();
 | 
				
			||||||
    info!("wrote Hello, starting echo");
 | 
					    info!("wrote Hello, starting echo");
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"]  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
 | 
					embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"]  }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										27
									
								
								examples/stm32l4/src/bin/mco.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								examples/stm32l4/src/bin/mco.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					#![no_std]
 | 
				
			||||||
 | 
					#![no_main]
 | 
				
			||||||
 | 
					#![feature(type_alias_impl_trait)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use defmt::*;
 | 
				
			||||||
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
 | 
					use embassy_stm32::gpio::{Level, Output, Speed};
 | 
				
			||||||
 | 
					use embassy_stm32::rcc::{Mco, Mco1Source, McoClock};
 | 
				
			||||||
 | 
					use embassy_time::{Duration, Timer};
 | 
				
			||||||
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[embassy_executor::main]
 | 
				
			||||||
 | 
					async fn main(_spawner: Spawner) {
 | 
				
			||||||
 | 
					    let p = embassy_stm32::init(Default::default());
 | 
				
			||||||
 | 
					    info!("Hello World!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let _mco = Mco::new(p.MCO, p.PA8, Mco1Source::Hsi16, McoClock::DIV1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut led = Output::new(p.PB14, Level::High, Speed::Low);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loop {
 | 
				
			||||||
 | 
					        led.set_high();
 | 
				
			||||||
 | 
					        Timer::after(Duration::from_millis(300)).await;
 | 
				
			||||||
 | 
					        led.set_low();
 | 
				
			||||||
 | 
					        Timer::after(Duration::from_millis(300)).await;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"]  }
 | 
				
			||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
					embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ]  }
 | 
				
			||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
					embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"]  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"]  }
 | 
				
			||||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] }
 | 
					embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] }
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "wasm", "nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wasm-logger = "0.2.0"
 | 
					wasm-logger = "0.2.0"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
# Before upgrading check that everything is available on all tier1 targets here:
 | 
					# Before upgrading check that everything is available on all tier1 targets here:
 | 
				
			||||||
# https://rust-lang.github.io/rustup-components-history
 | 
					# https://rust-lang.github.io/rustup-components-history
 | 
				
			||||||
[toolchain]
 | 
					[toolchain]
 | 
				
			||||||
channel = "nightly-2023-02-07"
 | 
					channel = "nightly-2023-04-02"
 | 
				
			||||||
components = [ "rust-src", "rustfmt", "llvm-tools-preview" ]
 | 
					components = [ "rust-src", "rustfmt", "llvm-tools-preview" ]
 | 
				
			||||||
targets = [
 | 
					targets = [
 | 
				
			||||||
    "thumbv7em-none-eabi",
 | 
					    "thumbv7em-none-eabi",
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 | 
					embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] }
 | 
				
			||||||
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
 | 
					embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
 | 
				
			||||||
embedded-io = { version = "0.4.0", features = ["async"] }
 | 
					embedded-io = { version = "0.4.0", features = ["async"] }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										25
									
								
								tests/nrf/src/bin/timer.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								tests/nrf/src/bin/timer.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					#![no_std]
 | 
				
			||||||
 | 
					#![no_main]
 | 
				
			||||||
 | 
					#![feature(type_alias_impl_trait)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use defmt::{assert, info};
 | 
				
			||||||
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
 | 
					use embassy_time::{Duration, Instant, Timer};
 | 
				
			||||||
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[embassy_executor::main]
 | 
				
			||||||
 | 
					async fn main(_spawner: Spawner) {
 | 
				
			||||||
 | 
					    let _p = embassy_nrf::init(Default::default());
 | 
				
			||||||
 | 
					    info!("Hello World!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let start = Instant::now();
 | 
				
			||||||
 | 
					    Timer::after(Duration::from_millis(100)).await;
 | 
				
			||||||
 | 
					    let end = Instant::now();
 | 
				
			||||||
 | 
					    let ms = (end - start).as_millis();
 | 
				
			||||||
 | 
					    info!("slept for {} ms", ms);
 | 
				
			||||||
 | 
					    assert!(ms >= 99);
 | 
				
			||||||
 | 
					    assert!(ms < 110);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    info!("Test OK");
 | 
				
			||||||
 | 
					    cortex_m::asm::bkpt();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] }
 | 
				
			||||||
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"]  }
 | 
					embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"]  }
 | 
				
			||||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 | 
					embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,7 @@ stm32u585ai = ["embassy-stm32/stm32u585ai"]     # IoT board
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
					embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 | 
				
			||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 | 
					embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
				
			||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] }
 | 
					embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] }
 | 
				
			||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"]  }
 | 
					embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"]  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user