atecc608b/hal.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//! Hardware Abstraction Layer (HAL) the driver depends on.
17//!
18//! Concrete backends (RP2040, STM32, host-side mock, etc.) implement
19//! [`AteccHal`]. The driver itself never touches a register directly. Every
20//! external action (I2C transfer, GPIO toggle, time delay) is mediated through
21//! this trait.
22//!
23//! The trait is `async` because polling an ATECC608B for a `Sign` command may
24//! take up to 220 ms. Doing this synchronously would starve other Embassy
25//! tasks (USB, button handling, LED animation). The same trait can be wired up
26//! to a non-Embassy executor as long as it understands `core::future::Future`.
27
28use core::fmt::Debug;
29
30/// Hardware operations the driver needs.
31///
32/// All operations are `async` to play nicely with Embassy. A backend that runs
33/// on a blocking platform can return `core::future::ready(_)`.
34///
35/// `async fn` in a public trait normally triggers a lint because the returned
36/// future is not `Send` and the caller has no way to add that bound. We
37/// silence it deliberately. The driver and its backends are all consumed by a
38/// single-threaded Embassy executor, where `Send` is irrelevant.
39#[allow(async_fn_in_trait)]
40pub trait AteccHal
41{
42 /// Backend-specific error type (`embassy_rp::i2c::Error`, a mock variant,
43 /// etc.).
44 type Error: Debug;
45
46 /// Write a buffer to the chip over I2C.
47 ///
48 /// `device_addr` is the 7-bit slave address (typically `0x60` for the
49 /// ATECC608B-SSHDA at default factory configuration).
50 async fn i2c_write(
51 &mut self,
52 device_addr: u8,
53 data: &[u8],
54 ) -> Result<(), Self::Error>;
55
56 /// Read a buffer from the chip over I2C. `buf` is filled exactly to its
57 /// length.
58 async fn i2c_read(
59 &mut self,
60 device_addr: u8,
61 buf: &mut [u8],
62 ) -> Result<(), Self::Error>;
63
64 /// Pull the SDA line low for `duration_us` microseconds.
65 ///
66 /// This is the wake pulse. The ATECC608B leaves deep sleep when SDA is
67 /// held low for at least `tWLO` (about 60 us). The backend is responsible
68 /// for temporarily detaching SDA from the I2C controller, driving it as a
69 /// plain GPIO output, then restoring it. Callers must follow this with a
70 /// 1.5 ms delay before issuing the first I2C transfer (see [`crate::wake`]).
71 async fn pulse_sda_low(
72 &mut self,
73 duration_us: u32,
74 ) -> Result<(), Self::Error>;
75
76 /// Sleep for `duration_us` microseconds.
77 ///
78 /// On the RP2040 implementation this is backed by `embassy_time::Timer`.
79 /// On the mock HAL it advances a simulated clock.
80 async fn delay_us(
81 &mut self,
82 duration_us: u32,
83 );
84
85 /// Sleep for `duration_ms` milliseconds.
86 async fn delay_ms(
87 &mut self,
88 duration_ms: u32,
89 );
90}