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}