1#![no_std]
2#![warn(missing_docs)]
3
4pub mod crc;
11pub mod frame;
13pub(crate) mod sync;
14
15pub use frame::{Data, EraseData, Flags, InfoData, MAX_PAYLOAD, VerifyData};
16
17use bitflags::bitflags;
18
19bitflags! {
20 #[derive(Clone, Copy, Debug, PartialEq)]
22 pub struct WriteFlags: u8 {
23 const FLUSH = 1 << 7;
27 }
28}
29
30bitflags! {
31 #[derive(Clone, Copy, Debug, PartialEq)]
33 pub struct ResetFlags: u8 {
34 const BOOTLOADER = 1 << 0;
36 }
37}
38
39pub const fn pack_version(major: u8, minor: u8, patch: u8) -> u16 {
45 ((major as u16) << 11) | ((minor as u16) << 6) | (patch as u16)
46}
47
48pub const fn unpack_version(v: u16) -> (u8, u8, u8) {
50 let major = (v >> 11) as u8 & 0x1F;
51 let minor = (v >> 6) as u8 & 0x1F;
52 let patch = v as u8 & 0x3F;
53 (major, minor, patch)
54}
55
56pub const fn const_parse_u8(s: &str) -> u8 {
59 let bytes = s.as_bytes();
60 let mut i = 0;
61 let mut result: u16 = 0;
62 while i < bytes.len() {
63 let d = bytes[i];
64 assert!(d >= b'0' && d <= b'9', "non-digit in version string");
65 result = result * 10 + (d - b'0') as u16;
66 i += 1;
67 }
68 assert!(result <= 255, "version component exceeds u8");
69 result as u8
70}
71
72#[macro_export]
77macro_rules! pkg_version {
78 () => {
79 $crate::pack_version(
80 $crate::const_parse_u8(env!("CARGO_PKG_VERSION_MAJOR")),
81 $crate::const_parse_u8(env!("CARGO_PKG_VERSION_MINOR")),
82 $crate::const_parse_u8(env!("CARGO_PKG_VERSION_PATCH")),
83 )
84 };
85}
86
87#[repr(u8)]
89#[derive(Debug, Clone, Copy, PartialEq)]
90pub enum Cmd {
91 Info = 0x00,
93 Erase = 0x01,
95 Write = 0x02,
97 Verify = 0x03,
99 Reset = 0x04,
101}
102
103impl Cmd {
104 pub fn is_valid(b: u8) -> bool {
106 b <= 0x04
107 }
108}
109
110#[repr(u8)]
112#[derive(Debug, Clone, Copy, PartialEq)]
113pub enum Status {
114 Request = 0x00,
116 Ok = 0x01,
118 WriteError = 0x02,
120 CrcMismatch = 0x03,
122 AddrOutOfBounds = 0x04,
124 Unsupported = 0x05,
126 PayloadOverflow = 0x06,
128}
129
130impl Status {
131 pub fn is_valid(b: u8) -> bool {
133 b <= 0x06
134 }
135}
136
137#[derive(Debug, PartialEq)]
143pub struct ReadError;
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn cmd_is_valid() {
151 assert!(Cmd::is_valid(Cmd::Info as u8));
152 assert!(Cmd::is_valid(Cmd::Reset as u8));
153 assert!(!Cmd::is_valid(0x05));
154 assert!(!Cmd::is_valid(0xFF));
155 }
156
157 #[test]
158 fn status_is_valid() {
159 assert!(Status::is_valid(Status::Request as u8));
160 assert!(Status::is_valid(Status::Unsupported as u8));
161 assert!(Status::is_valid(Status::PayloadOverflow as u8));
162 assert!(!Status::is_valid(0x07));
163 assert!(!Status::is_valid(0xFF));
164 }
165
166 #[test]
167 fn pack_unpack_round_trip() {
168 assert_eq!(unpack_version(pack_version(0, 0, 1)), (0, 0, 1));
169 assert_eq!(unpack_version(pack_version(1, 2, 3)), (1, 2, 3));
170 assert_eq!(unpack_version(pack_version(31, 31, 63)), (31, 31, 63));
171 assert_eq!(pack_version(0, 0, 0), 0);
172 }
173
174 #[test]
175 fn erased_flash_sentinel() {
176 let (m, n, p) = unpack_version(0xFFFF);
178 assert_eq!((m, n, p), (31, 31, 63));
179 }
180
181 #[test]
182 fn pkg_version_macro() {
183 let v = pkg_version!();
184 let expected = pack_version(
185 const_parse_u8(env!("CARGO_PKG_VERSION_MAJOR")),
186 const_parse_u8(env!("CARGO_PKG_VERSION_MINOR")),
187 const_parse_u8(env!("CARGO_PKG_VERSION_PATCH")),
188 );
189 assert_eq!(v, expected);
190 }
191}