Skip to main content

atecc608b/
wake.rs

1// Copyright (c) 2026 Tuloup Simon
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program. If not, see <https://www.gnu.org/licenses/>.
15
16//! Wake, idle, and sleep sequences for the ATECC608B.
17//!
18//! The chip spends most of its life in deep sleep (under 150 nA). To talk to
19//! it the driver must first perform a wake sequence. After commands have been
20//! issued, the driver should put the chip back to idle or sleep before its
21//! watchdog (about 1.3 s nominal) elapses on its own.
22//!
23//! The functions in this module are agnostic to the high-level command flow.
24//! They live one level below [`crate::driver::Atecc`] and operate directly on
25//! a HAL plus an I2C address.
26//!
27//! # Wake protocol
28//!
29//! 1. Pull SDA low for at least
30//!    [`crate::opcodes::WAKE_LOW_DURATION_US`] microseconds.
31//! 2. Release SDA and wait [`crate::opcodes::WAKE_DELAY_US`] microseconds.
32//! 3. Read 4 bytes back over I2C. They must equal
33//!    [`crate::opcodes::WAKE_RESPONSE_OK`] (`04 11 33 43`).
34//!
35//! If the chip's power-on self-test failed, the response is
36//! [`crate::opcodes::WAKE_RESPONSE_SELFTEST_FAIL`] (`04 07 C4 40`) instead.
37//!
38//! # Idle and sleep
39//!
40//! - **Idle** preserves the contents of `TempKey` and the random number
41//!   generator state. Useful between two related commands.
42//! - **Sleep** clears volatile state and brings the chip back to its low
43//!   power consumption level.
44//!
45//! Both are issued as a single I2C write of the corresponding word address
46//! byte, with no payload.
47
48use crate::error::AteccError;
49use crate::hal::AteccHal;
50use crate::opcodes::
51{
52    WAKE_DELAY_US,
53    WAKE_LOW_DURATION_US,
54    WAKE_RESPONSE_OK,
55    WAKE_RESPONSE_SELFTEST_FAIL,
56    WORD_ADDRESS_IDLE,
57    WORD_ADDRESS_SLEEP,
58};
59
60/// Perform the wake sequence and verify the chip's response.
61///
62/// On success the chip is awake and ready to receive a command.
63///
64/// # Errors
65/// - [`AteccError::WakeFailed`] if the response does not match
66///   [`WAKE_RESPONSE_OK`].
67/// - [`AteccError::SelfTestFailure`] if the response is the self-test failure
68///   pattern. The chip is unusable until the next power cycle.
69/// - [`AteccError::Hal`] if the HAL itself reports an I2C or GPIO error.
70pub(crate) async fn wake<H>(hal: &mut H, device_addr: u8) -> Result<(), AteccError<H::Error>>
71where
72    H: AteccHal,
73{
74    // Step 1: hold SDA low long enough for the chip to detect a wake pulse.
75    hal.pulse_sda_low(WAKE_LOW_DURATION_US).await?;
76
77    // Step 2: let the chip's internal logic come up.
78    hal.delay_us(WAKE_DELAY_US).await;
79
80    // Step 3: read 4 bytes and compare against the known good and known bad
81    // patterns.
82    let mut response = [0u8; 4];
83    hal.i2c_read(device_addr, &mut response).await?;
84
85    if response == WAKE_RESPONSE_OK
86    {
87        Ok(())
88    }
89    else if response == WAKE_RESPONSE_SELFTEST_FAIL
90    {
91        Err(AteccError::SelfTestFailure)
92    }
93    else
94    {
95        Err(AteccError::WakeFailed)
96    }
97}
98
99/// Put the chip into idle.
100///
101/// Idle preserves volatile state (`TempKey`, RNG seed) but resets the watchdog.
102/// Useful between two commands that share `TempKey`, like Nonce followed by
103/// Sign.
104///
105/// # Errors
106/// [`AteccError::Hal`] if the I2C write fails.
107pub(crate) async fn idle<H>(hal: &mut H, device_addr: u8) -> Result<(), AteccError<H::Error>>
108where
109    H: AteccHal,
110{
111    hal.i2c_write(device_addr, &[WORD_ADDRESS_IDLE]).await?;
112    Ok(())
113}
114
115/// Put the chip into deep sleep.
116///
117/// Sleep clears volatile state (`TempKey`, RNG seed) and brings the chip
118/// to its lowest power consumption level. The next command requires a
119/// fresh wake sequence (which is the same cost as wake-from-idle).
120///
121/// # Errors
122/// [`AteccError::Hal`] if the I2C write fails.
123pub(crate) async fn sleep<H>(hal: &mut H, device_addr: u8) -> Result<(), AteccError<H::Error>>
124where
125    H: AteccHal,
126{
127    hal.i2c_write(device_addr, &[WORD_ADDRESS_SLEEP]).await?;
128    Ok(())
129}