Skip to main content

yapu/
protocol.rs

1#[allow(unused_imports)]
2use binrw::{BinRead, BinWrite, binread, binrw, binwrite};
3use std::borrow::Cow;
4use std::ops::RangeInclusive;
5use std::ops::{Deref, DerefMut};
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9#[allow(unused_imports)]
10#[cfg(feature = "serde")]
11use serde::{de, de::Error as _, ser};
12
13/// Protocol conversion error
14#[derive(Debug, Clone)]
15pub enum Error {
16    Exceeded(Exceeded),
17}
18
19impl Error {
20    pub fn is_exceeded(&self) -> bool {
21        matches!(self, Self::Exceeded(..))
22    }
23
24    pub fn exceeded(&self) -> Option<&Exceeded> {
25        match &self {
26            Self::Exceeded(e) => Some(e),
27        }
28    }
29}
30
31impl From<Exceeded> for Error {
32    fn from(value: Exceeded) -> Self {
33        Self::Exceeded(value)
34    }
35}
36
37impl std::fmt::Display for Error {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        match self {
40            Self::Exceeded(e) => write!(f, "exceeded: {}", e),
41        }
42    }
43}
44
45impl std::error::Error for Error {}
46
47#[derive(Debug, Clone)]
48pub struct Exceeded(usize, ExpectedRange);
49
50impl Exceeded {
51    pub fn unexpected(&self) -> usize {
52        self.0
53    }
54    pub fn expected_range(&self) -> &RangeInclusive<usize> {
55        &self.1.0
56    }
57
58    #[cfg(feature = "serde")]
59    pub fn to_serde<'de, D: Deserializer<'de>>(&self) -> D::Error {
60        D::Error::invalid_value(de::Unexpected::Unsigned(self.0 as u64), &self.1)
61    }
62}
63
64impl std::fmt::Display for Exceeded {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        write!(
67            f,
68            "{} is not within valid range of size ({})",
69            self.0, self.1
70        )
71    }
72}
73
74impl std::error::Error for Exceeded {}
75
76/// A wrapper of [`RangeInclusive<usize>`] that implements [`de::Expected`], for
77/// friendlier error handling during deserialization.
78#[derive(Debug, Clone, PartialEq, Eq)]
79struct ExpectedRange(RangeInclusive<usize>);
80
81impl From<RangeInclusive<usize>> for ExpectedRange {
82    fn from(value: RangeInclusive<usize>) -> Self {
83        Self(value)
84    }
85}
86
87impl std::fmt::Display for ExpectedRange {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        write!(f, "{:?}", self.0)
90    }
91}
92
93#[cfg(feature = "serde")]
94impl de::Expected for ExpectedRange {
95    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
96        write!(formatter, "a size within range {}", self)
97    }
98}
99
100mod checksum {
101    #[derive(Default, Debug, Clone)]
102    pub struct Buffer {
103        state: u8,
104    }
105
106    impl Buffer {
107        pub fn new() -> Self {
108            Self::default()
109        }
110        pub fn state(&self) -> u8 {
111            self.state
112        }
113    }
114
115    impl std::io::Write for Buffer {
116        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
117            self.state = self.state ^ iter(buf.iter().copied());
118            Ok(buf.len())
119        }
120        fn flush(&mut self) -> std::io::Result<()> {
121            Ok(())
122        }
123    }
124
125    pub(super) fn single(data: u8) -> u8 {
126        data ^ 0xff
127    }
128
129    pub(super) fn iter(data: impl Iterator<Item = u8>) -> u8 {
130        data.fold(0u8, |acc, e| acc ^ e)
131    }
132}
133
134/// A wrapper type for opcode.
135///
136/// `binrw` only supports magic literals, which means any computed value is not
137/// supported, no matter it's constant or not. Therefore it's not possible to
138/// write:
139///
140/// ```ignore
141/// #[derive(BinWrite)]
142/// #[bw(big)]
143/// enum Command {
144///     #[bw(magic = (0x00u8 << 8) ^ (0x00u8 ^ 0xffu8))]
145///     Get,
146/// }
147/// ```
148///
149/// The workaround here is to define a new wrapper type for opcodes and add a
150/// checksum field with computed `binrw` values, which requires using procedural
151/// macro `binwrite` rather than derive macro `BinWrite`.
152#[binwrite]
153#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
154#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
155#[bw(big)]
156pub struct Opcode(u8, #[bw(calc = checksum::single(self.0))] u8);
157
158impl Opcode {
159    pub const GET: Self = Self(0x00u8);
160    pub const GET_VERSION: Self = Self(0x01u8);
161    pub const GET_ID: Self = Self(0x02u8);
162    pub const READ: Self = Self(0x11u8);
163    pub const GO: Self = Self(0x21u8);
164    pub const WRITE: Self = Self(0x31u8);
165    pub const ERASE: Self = Self(0x43u8);
166    pub const EXTENDED_ERASE: Self = Self(0x44u8);
167    pub const WRITE_PROTECT: Self = Self(0x63u8);
168    pub const WRITE_UNPROTECT: Self = Self(0x73u8);
169    pub const READ_PROTECT: Self = Self(0x82u8);
170    pub const READ_UNPROTECT: Self = Self(0x92u8);
171    pub const GET_CHECKSUM: Self = Self(0xa1u8);
172    pub const SPECIAL: Self = Self(0x50u8);
173    pub const EXTENDED_SPECIAL: Self = Self(0x51u8);
174
175    pub fn as_u8(&self) -> u8 {
176        self.0
177    }
178}
179
180impl std::fmt::Display for Opcode {
181    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182        match self {
183            &Self::GET => write!(f, "GET"),
184            &Self::GET_VERSION => write!(f, "GET_VERSION"),
185            &Self::GET_ID => write!(f, "GET_ID"),
186            &Self::READ => write!(f, "READ"),
187            &Self::GO => write!(f, "GO"),
188            &Self::WRITE => write!(f, "WRITE"),
189            &Self::ERASE => write!(f, "ERASE"),
190            &Self::EXTENDED_ERASE => write!(f, "EXTENDED_ERASE"),
191            &Self::WRITE_PROTECT => write!(f, "WRITE_PROTECT"),
192            &Self::WRITE_UNPROTECT => write!(f, "WRITE_UNPROTECT"),
193            &Self::READ_PROTECT => write!(f, "READ_PROTECT"),
194            &Self::READ_UNPROTECT => write!(f, "READ_UNPROTECT"),
195            &Self::GET_CHECKSUM => write!(f, "GET_CHECKSUM"),
196            &Self::SPECIAL => write!(f, "SPECIAL"),
197            &Self::EXTENDED_SPECIAL => write!(f, "EXTENDED_SPECIAL"),
198            opcode => write!(f, "UNKNOWN ({:02x?})", opcode.as_u8()),
199        }
200    }
201}
202
203impl From<u8> for Opcode {
204    fn from(value: u8) -> Self {
205        Self(value)
206    }
207}
208
209/// Address
210#[binwrite]
211#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
212#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
213#[bw(big)]
214pub struct Address(
215    u32,
216    #[bw(calc = checksum::iter(self.0.to_ne_bytes().iter().copied()))] u8,
217);
218
219impl Address {
220    pub fn as_u32(&self) -> u32 {
221        self.0
222    }
223}
224
225impl From<u32> for Address {
226    fn from(value: u32) -> Self {
227        Self(value)
228    }
229}
230
231impl Into<u32> for Address {
232    fn into(self) -> u32 {
233        self.0
234    }
235}
236
237macro_rules! define_slice_item {
238    ($vis:vis $name:ident($inner_ty:ident), $as_method:ident, $size_ty:ty, $size_range:expr) => {
239        #[derive(BinWrite, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
240        $vis struct $name;
241
242        impl SliceItem for $name {
243            type Repr = $inner_ty;
244            type Size = $size_ty;
245            const SIZE_RANGE: RangeInclusive<usize> = $size_range;
246        }
247    }
248}
249
250pub type PageNo = u8;
251pub type ExtendedPageNo = u16;
252pub type SectorNo = u8;
253
254define_slice_item! { pub Byte(u8), as_u8, u8, 1..=256 }
255define_slice_item! { pub Page(PageNo), as_u8, u8, 1..=256 }
256define_slice_item! { pub ExtendedPage(ExtendedPageNo), as_u16, u16, 1..=0xff00 }
257define_slice_item! { pub Sector(SectorNo), as_u8, u8, 1..=256 }
258
259#[binwrite]
260#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
261#[bw(big)]
262pub struct Size(u8, #[bw(calc = checksum::single(self.0))] u8);
263
264impl Into<usize> for Size {
265    fn into(self) -> usize {
266        self.0 as usize + <Byte as SliceItem>::SIZE_RANGE.start()
267    }
268}
269
270impl TryFrom<usize> for Size {
271    type Error = Error;
272
273    fn try_from(value: usize) -> Result<Self, Self::Error> {
274        let range = <Byte as SliceItem>::SIZE_RANGE;
275        if range.contains(&value) {
276            Ok(Self(value as u8))
277        } else {
278            Err(Exceeded(value, range.into()).into())
279        }
280    }
281}
282
283#[cfg(feature = "serde")]
284impl<'de> Deserialize<'de> for Size {
285    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
286    where
287        D: Deserializer<'de>,
288    {
289        let value = usize::deserialize(deserializer)?;
290        let converted = Self::try_from(value).map_err(|e| {
291            let exceeded = e.exceeded().unwrap();
292            exceeded.to_serde::<D>()
293        })?;
294        Ok(converted)
295    }
296}
297
298#[cfg(feature = "serde")]
299impl serde::Serialize for Size {
300    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
301    where
302        S: Serializer,
303    {
304        serializer.serialize_u64(self.0 as u64)
305    }
306}
307
308pub trait SliceItem {
309    #[cfg(not(feature = "serde"))]
310    type Repr: Copy + Clone;
311    #[cfg(feature = "serde")]
312    type Repr: Copy + Clone + Serialize + for<'de> Deserialize<'de>;
313    type Size: TryFrom<usize>;
314    const SIZE_RANGE: RangeInclusive<usize> = usize::MIN..=usize::MAX;
315}
316
317#[derive(Debug, Clone)]
318#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
319pub struct Slice<'a, T: SliceItem> {
320    inner: Cow<'a, [T::Repr]>,
321}
322
323impl<'a, T: SliceItem> Deref for Slice<'a, T> {
324    type Target = Cow<'a, [T::Repr]>;
325
326    fn deref(&self) -> &Self::Target {
327        &self.inner
328    }
329}
330
331impl<'a, T: SliceItem> DerefMut for Slice<'a, T> {
332    fn deref_mut(&mut self) -> &mut Self::Target {
333        &mut self.inner
334    }
335}
336
337impl<'a, T: SliceItem> Slice<'a, T> {
338    /// Consumes [`Slice`] and returns the inner [`Cow`].
339    pub fn into_inner(self) -> Cow<'a, [T::Repr]> {
340        self.inner
341    }
342
343    /// Returns slice of specific slice items.
344    pub fn as_slice(&self) -> &[T::Repr] {
345        &self.inner
346    }
347}
348
349impl<'a, T: SliceItem> Into<Cow<'a, [T::Repr]>> for Slice<'a, T> {
350    fn into(self) -> Cow<'a, [T::Repr]> {
351        self.inner
352    }
353}
354
355impl<'a, T: SliceItem> TryFrom<Cow<'a, [T::Repr]>> for Slice<'a, T> {
356    type Error = Error;
357
358    fn try_from(value: Cow<'a, [T::Repr]>) -> Result<Self, Self::Error> {
359        if T::SIZE_RANGE.contains(&value.len()) {
360            Ok(Self { inner: value })
361        } else {
362            Err(Exceeded(value.len(), T::SIZE_RANGE.into()).into())
363        }
364    }
365}
366
367impl<'a, T: SliceItem> TryFrom<Vec<T::Repr>> for Slice<'a, T> {
368    type Error = Error;
369
370    fn try_from(value: Vec<T::Repr>) -> Result<Self, Self::Error> {
371        if T::SIZE_RANGE.contains(&value.len()) {
372            Ok(Self {
373                inner: value.into(),
374            })
375        } else {
376            Err(Exceeded(value.len(), T::SIZE_RANGE.into()).into())
377        }
378    }
379}
380
381impl<'a, T: SliceItem> TryFrom<&'a [T::Repr]> for Slice<'a, T> {
382    type Error = Error;
383
384    fn try_from(value: &'a [T::Repr]) -> Result<Self, Self::Error> {
385        if T::SIZE_RANGE.contains(&value.len()) {
386            Ok(Self {
387                inner: value.into(),
388            })
389        } else {
390            Err(Exceeded(value.len(), T::SIZE_RANGE.into()).into())
391        }
392    }
393}
394
395impl<'a, T: SliceItem + BinWrite<Args<'a> = ()>> BinWrite for Slice<'a, T>
396where
397    [T::Repr]: BinWrite<Args<'a> = ()>,
398    T::Size: BinWrite<Args<'a> = ()>,
399    <T::Size as TryFrom<usize>>::Error: std::fmt::Debug,
400{
401    type Args<'arg> = ();
402
403    fn write_options<W: std::io::Write + std::io::Seek>(
404        &self,
405        writer: &mut W,
406        endian: binrw::Endian,
407        args: Self::Args<'_>,
408    ) -> binrw::BinResult<()> {
409        use binrw::io::NoSeek;
410
411        // write shifted size
412        let lowerbound = *<T as SliceItem>::SIZE_RANGE.start();
413        let size = <T as SliceItem>::Size::try_from(self.inner.len() - lowerbound).unwrap();
414        size.write_options(writer, endian, args)?;
415
416        // write data
417        self.inner.write_options(writer, endian, args)?;
418
419        // write checksum
420        let mut buffer = checksum::Buffer::new();
421        self.inner
422            .write_options(&mut NoSeek::new(&mut buffer), endian, args)?;
423        buffer.state().write_options(writer, endian, args)?;
424
425        Ok(())
426    }
427}
428
429impl<'a, T: SliceItem + BinWrite<Args<'a> = ()>> binrw::meta::WriteEndian for Slice<'a, T>
430where
431    [T::Repr]: BinWrite<Args<'a> = ()>,
432    T::Size: BinWrite<Args<'a> = ()>,
433    <T::Size as TryFrom<usize>>::Error: std::fmt::Debug,
434{
435    const ENDIAN: binrw::meta::EndianKind = binrw::meta::EndianKind::Endian(binrw::Endian::Big);
436}
437
438pub type Data<'a> = Slice<'a, Byte>;
439pub type PageNos<'a> = Slice<'a, Page>;
440pub type ExtendedPageNos<'a> = Slice<'a, ExtendedPage>;
441pub type SectorNos<'a> = Slice<'a, Sector>;
442
443/// Command
444#[binwrite]
445#[derive(Debug, Clone)]
446#[bw(big)]
447pub enum Command<'a> {
448    Get(#[bw(calc = Opcode::GET)] Opcode),
449    Version(#[bw(calc = Opcode::GET_VERSION)] Opcode),
450    Id(#[bw(calc = Opcode::GET_ID)] Opcode),
451    Read {
452        #[bw(calc = Opcode::READ)]
453        opcode: Opcode,
454        address: Address,
455        size: Size,
456    },
457    Go(#[bw(calc = Opcode::GO)] Opcode, Address),
458    Write {
459        #[bw(calc = Opcode::WRITE)]
460        opcode: Opcode,
461        address: Address,
462        data: Data<'a>,
463    },
464    Erase(#[bw(calc = Opcode::ERASE)] Opcode, Erase<'a>),
465    ExtendedErase(#[bw(calc = Opcode::ERASE)] Opcode, ExtendedErase<'a>),
466    WriteProtect(#[bw(calc = Opcode::WRITE_PROTECT)] Opcode),
467    WriteUnprotect(#[bw(calc = Opcode::WRITE_UNPROTECT)] Opcode),
468    ReadProtect(#[bw(calc = Opcode::READ_PROTECT)] Opcode),
469    ReadUnprotect(#[bw(calc = Opcode::READ_UNPROTECT)] Opcode),
470
471    /// This is used for baudrate handshaking.
472    #[bw(magic = 0x7fu8)]
473    Synchronize,
474}
475
476/// Command for [`Opcode::ERASE`].
477#[derive(BinWrite, Debug, Clone)]
478#[bw(big)]
479pub enum Erase<'a> {
480    #[bw(magic = 0xff00u16)]
481    Global,
482    Specific(Slice<'a, Page>),
483}
484
485impl<'a> Erase<'a> {
486    /// Whether erasure is done globally.
487    pub fn is_global(self) -> bool {
488        matches!(self, Self::Global)
489    }
490
491    /// Whether erasure is done on specific pages.
492    pub fn is_specific(self) -> bool {
493        matches!(self, Self::Specific(..))
494    }
495
496    /// Returns pages if the erasure is not global.
497    pub fn pages(&self) -> Option<&[PageNo]> {
498        match self {
499            Self::Global => None,
500            Self::Specific(slice) => Some(slice.as_slice()),
501        }
502    }
503}
504
505/// Command for [`Opcode::EXTENDED_ERASE`].
506#[derive(BinWrite, Debug, Clone)]
507#[bw(big)]
508pub enum ExtendedErase<'a> {
509    #[bw(magic = b"\xff\xff\x00")]
510    Global,
511    #[bw(magic = b"\xff\xfe\x01")]
512    Bank1,
513    #[bw(magic = b"\xff\xfd\x02")]
514    Bank2,
515    Specific(Slice<'a, ExtendedPage>),
516}
517
518impl<'a> ExtendedErase<'a> {
519    /// Whether erasure is done globally.
520    pub fn is_global(self) -> bool {
521        matches!(self, Self::Global)
522    }
523
524    /// Whether erasure is done on bank 1.
525    pub fn is_bank1(self) -> bool {
526        matches!(self, Self::Bank1)
527    }
528
529    /// Whether erasure is done on bank 2.
530    pub fn is_bank2(self) -> bool {
531        matches!(self, Self::Bank2)
532    }
533
534    /// Whether erasure is done on specific pages.
535    pub fn is_specific(self) -> bool {
536        matches!(self, Self::Specific(..))
537    }
538
539    /// Returns pages if the erasure is not global.
540    pub fn pages(&self) -> Option<&[ExtendedPageNo]> {
541        match self {
542            Self::Global => None,
543            Self::Bank1 => None,
544            Self::Bank2 => None,
545            Self::Specific(slice) => Some(slice.as_slice()),
546        }
547    }
548}
549
550/// Reply
551#[derive(BinRead, Debug, Clone, Copy)]
552#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
553#[br(big)]
554pub enum Reply {
555    /// ACK
556    #[brw(magic = 0x79u8)]
557    Ack,
558    /// Negative
559    #[brw(magic = 0x1fu8)]
560    NAck,
561}
562
563/// Bootloader information
564///
565/// Contains version and supported [`Opcode`]s.
566#[binread]
567#[derive(Debug, Clone)]
568#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
569#[br(big)]
570pub struct Bootloader {
571    #[br(temp)]
572    len: u8,
573    version: u8,
574    #[br(count = len, map = |data: Vec<u8>| {
575        data.into_iter().map(|v| v.into()).collect()
576    })]
577    opcodes: Vec<Opcode>,
578}
579
580impl Bootloader {
581    /// Bootloader version in [`u8`].
582    #[inline]
583    pub fn version(&self) -> u8 {
584        self.version
585    }
586
587    /// Bootloader major version.
588    #[inline]
589    pub fn major(&self) -> u8 {
590        self.version >> 4
591    }
592
593    /// Bootloader minor version.
594    #[inline]
595    pub fn minor(&self) -> u8 {
596        self.version & 0xf
597    }
598
599    /// Bootloader version string.
600    pub fn version_string(&self) -> String {
601        format!("{}.{}", self.major(), self.minor())
602    }
603
604    /// Supported [`Opcode`]s of the bootloader.
605    #[inline]
606    pub fn opcodes(&self) -> &[Opcode] {
607        &self.opcodes
608    }
609
610    /// Whether bootloader supports an [`Opcode`].
611    #[inline]
612    pub fn supports(&self, opcode: impl Into<Opcode>) -> bool {
613        self.opcodes.contains(&opcode.into())
614    }
615}
616
617/// Version
618#[derive(BinRead, Debug, Clone)]
619#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
620#[br(big)]
621pub struct Version {
622    version: u8,
623    options: [u8; 2],
624}
625
626impl Version {
627    /// Bootloader version in [`u8`].
628    #[inline]
629    pub fn version(&self) -> u8 {
630        self.version
631    }
632
633    /// Bootloader major version.
634    #[inline]
635    pub fn major(&self) -> u8 {
636        self.version >> 4
637    }
638
639    /// Bootloader minor version.
640    #[inline]
641    pub fn minor(&self) -> u8 {
642        self.version & 0xf
643    }
644
645    /// Bootloader version string.
646    pub fn version_string(&self) -> String {
647        format!("{}.{}", self.major(), self.minor())
648    }
649
650    #[inline]
651    pub fn options(&self) -> [u8; 2] {
652        self.options
653    }
654}
655
656impl std::fmt::Display for Version {
657    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
658        write!(f, "{}.{}", self.major(), self.minor())
659    }
660}
661
662/// Chip ID
663#[binread]
664#[derive(Debug, Clone)]
665#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
666#[br(big)]
667pub struct Id {
668    #[br(temp)]
669    len: u8,
670    #[br(count = len + 1)]
671    id: Vec<u8>,
672}
673
674impl Id {
675    /// Consumes [`Id`] and returns raw chip ID in [`Vec<u8>`].
676    #[inline]
677    pub fn into_id(self) -> Vec<u8> {
678        self.id
679    }
680
681    /// Returns raw chip ID in [`[u8]`] slice.
682    #[inline]
683    pub fn id(&self) -> &[u8] {
684        &self.id
685    }
686
687    /// Returns raw chip ID in [`[u8]`] slice.
688    #[inline]
689    pub fn as_slice(&self) -> &[u8] {
690        &self.id
691    }
692
693    /// Converts chip ID to a fixed-size array.
694    pub fn as_array<const N: usize>(&self) -> [u8; N] {
695        let mut buf: [u8; N] = [0u8; N];
696        buf[N - self.id.len()..].copy_from_slice(&self.id);
697        buf
698    }
699
700    /// Interprets chip ID as [`u16`].
701    #[inline]
702    pub fn as_u16(&self) -> u16 {
703        u16::from_be_bytes(self.as_array())
704    }
705
706    /// Interprets chip ID as [`u32`].
707    #[inline]
708    pub fn as_u32(&self) -> u32 {
709        u32::from_be_bytes(self.as_array())
710    }
711
712    /// Interprets chip ID as [`u64`].
713    #[inline]
714    pub fn as_u64(&self) -> u64 {
715        u64::from_be_bytes(self.as_array())
716    }
717}