minidsp_protocol/
commands.rs

1//! Commands sent to the device and their responses
2//!
3//! This module contains structs that can be serialized into commands being sent to the device.
4//! Each command implements the `UnaryCommand` trait which specifies the response type as an
5//! associated type.
6//!
7//! It's typical to use the [roundtrip] method in order to send the command to a transport and
8//! obtained its parsed response.
9
10use alloc::vec::Vec;
11use core::{convert::TryInto, fmt, fmt::Debug, ops::Deref, str::FromStr};
12use std::ops::DerefMut;
13
14use anyhow::Result;
15use bytes::{Buf, BufMut, Bytes, BytesMut};
16#[cfg(feature = "use_serde")]
17use serde::{Deserialize, Serialize};
18#[cfg(feature = "debug")]
19use thiserror::Error;
20
21use super::DeviceInfo;
22use crate::{
23    eeprom, packet::ParseError, util::TryBuf, util::TryBufError, FixedPoint, MasterStatus,
24};
25
26/// Maximum number of floats that can be read in a single command
27pub const READ_FLOATS_MAX: usize = 14;
28
29#[derive(Clone)]
30#[cfg_attr(feature = "debug", derive(Debug, Error))]
31pub enum ProtocolError {
32    #[cfg_attr(feature = "debug", error("bad cmd id"))]
33    BadCommandId,
34
35    #[cfg_attr(feature = "debug", error("unexpected response type"))]
36    UnexpectedResponseType,
37
38    #[cfg_attr(feature = "debug", error("parse error: {0}"))]
39    ParseError(ParseError),
40
41    #[cfg_attr(
42        feature = "debug",
43        error("the received hardware id was malformed or empty")
44    )]
45    MalformedHardwareId,
46
47    #[cfg_attr(feature = "debug", error("parse error: {0}"))]
48    DecodeError(#[from] TryBufError),
49}
50
51#[derive(Clone)]
52pub struct BytesWrap(pub Bytes);
53impl fmt::Debug for BytesWrap {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        Debug::fmt(self.0.as_ref(), f)
56    }
57}
58impl Deref for BytesWrap {
59    type Target = Bytes;
60
61    fn deref(&self) -> &Self::Target {
62        &self.0
63    }
64}
65impl DerefMut for BytesWrap {
66    fn deref_mut(&mut self) -> &mut Self::Target {
67        &mut self.0
68    }
69}
70
71#[derive(Clone)]
72pub enum Value {
73    Unknown(Bytes),
74    Float(f32),
75    FixedPoint(FixedPoint),
76    Int(u16),
77    Int32(u32),
78}
79
80impl Default for Value {
81    fn default() -> Self {
82        Value::Float(0.)
83    }
84}
85
86impl From<f32> for Value {
87    fn from(f: f32) -> Self {
88        Value::Float(f)
89    }
90}
91
92impl From<FixedPoint> for Value {
93    fn from(fp: FixedPoint) -> Self {
94        Value::FixedPoint(fp)
95    }
96}
97
98impl From<u16> for Value {
99    fn from(x: u16) -> Self {
100        Value::Int(x)
101    }
102}
103
104impl Value {
105    pub fn into_bytes(self) -> Bytes {
106        match self {
107            Value::Unknown(b) => b,
108            Value::Float(f) => Bytes::copy_from_slice(&f.to_le_bytes()),
109            Value::FixedPoint(fp) => Bytes::copy_from_slice(&fp.to_u32().to_be_bytes()),
110            Value::Int(i) => {
111                let mut b = BytesMut::with_capacity(4);
112                b.put_u16_le(i);
113                b.put_u16(0x00);
114                b.freeze()
115            }
116            Value::Int32(i) => Bytes::copy_from_slice(&i.to_be_bytes()),
117        }
118    }
119
120    pub fn from_bytes(mut b: Bytes) -> Result<Self, TryBufError> {
121        Ok(
122            if b.len() == 4 && ((b[0] != 0 || b[1] != 0) && b[2] == 0 && b[3] == 0) {
123                Value::Int(b.try_get_u16_le()?)
124            } else {
125                Value::Unknown(b)
126            },
127        )
128    }
129}
130
131#[cfg(feature = "debug")]
132impl fmt::Debug for Value {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        let b = self.clone().into_bytes();
135        match self {
136            Value::Unknown(_) => {
137                let float = if b.len() >= 4 {
138                    Some(b.clone().get_f32_le())
139                } else {
140                    None
141                };
142
143                let u = if b.len() >= 4 {
144                    Some(b.clone().get_u32())
145                } else {
146                    None
147                };
148
149                let fixed = if b.len() >= 4 {
150                    Some(FixedPoint::from_u32(b.clone().get_u32()))
151                } else {
152                    None
153                };
154
155                let i = b[0];
156                write!(
157                    f,
158                    "Value {{ Bytes: {:x?} (i: {:?} | i32: {:?} | f32: {:?} | fp: {:?}) }}",
159                    b.as_ref(),
160                    i,
161                    u.unwrap_or_default(),
162                    float.unwrap_or_default(),
163                    fixed.unwrap_or_default()
164                )
165            }
166            &Value::Float(val) => {
167                write!(f, "Value {{ Bytes: {:x?} (f32: {:?}) }}", b.as_ref(), val)
168            }
169            &Value::FixedPoint(val) => {
170                write!(
171                    f,
172                    "Value {{ Bytes: {:x?} (fp: {:?}) }}",
173                    b.as_ref(),
174                    val.to_f32(),
175                )
176            }
177            &Value::Int(val) => {
178                write!(f, "Value {{ Bytes: {:x?} (i: {:?}) }}", b.as_ref(), val)
179            }
180            &Value::Int32(val) => {
181                write!(f, "Value {{ Bytes: {:x?} (i32: {:?}) }}", b.as_ref(), val)
182            }
183        }
184    }
185}
186
187#[derive(Clone, Copy, PartialEq, Eq)]
188#[cfg_attr(feature = "debug", derive(Debug))]
189pub struct Addr {
190    pub val: u32,
191    pub len: u8,
192    pub extra_bit: bool,
193}
194
195impl Addr {
196    pub fn new(val: u16, len: u8) -> Self {
197        Self {
198            val: val as u32,
199            len,
200            extra_bit: true,
201        }
202    }
203
204    pub fn new_with_extra_bit(val: u32, len: u8, extra_bit: bool) -> Self {
205        Self {
206            val,
207            len,
208            extra_bit,
209        }
210    }
211
212    pub fn read(buf: &mut Bytes, len: u8) -> Result<Self, TryBufError> {
213        match len {
214            2 => Self::read_u16(buf),
215            3 => Self::read_u24(buf),
216            _ => panic!("invalid address len"),
217        }
218    }
219
220    pub fn read_u16(buf: &mut Bytes) -> Result<Self, TryBufError> {
221        let mut val = buf.try_get_u16()?;
222        let extra_bit = (val & 0x80_00) != 0;
223
224        val &= 0x0FFF;
225
226        Ok(Self {
227            val: val as _,
228            len: 2,
229            extra_bit,
230        })
231    }
232
233    pub fn read_u24(buf: &mut Bytes) -> Result<Self, TryBufError> {
234        let prefix = buf.try_get_u8()?;
235        let extra_bit = (prefix & 0x80) != 0;
236        let prefix = prefix & 0x0F;
237        let addr = buf.try_get_u16()? as u32;
238        let val = (prefix as u32) << 16 | addr;
239        Ok(Self {
240            val: val as _,
241            len: 3,
242            extra_bit,
243        })
244    }
245
246    pub fn write(&self, buf: &mut BytesMut) {
247        match self.len {
248            2 => {
249                let mut val = 0x80_00 | (self.val & 0x7F_FF);
250                if !self.extra_bit {
251                    val ^= 0x80_00;
252                }
253                buf.put_u16(val as _)
254            }
255            3 => {
256                let mut val = 0x80_00_00 | (self.val & 0x7F_FF_FF);
257                if !self.extra_bit {
258                    val ^= 0x80_00_00;
259                }
260                buf.put_u8(((val >> 16) & 0xFF) as _);
261                buf.put_u16(val as _);
262            }
263            _ => panic!("invalid address len"),
264        }
265    }
266}
267
268#[derive(Clone)]
269#[cfg_attr(feature = "debug", derive(Debug))]
270pub enum Commands {
271    /// 0x31: Read hardware id
272    ReadHardwareId,
273
274    /// 0x14: Reads float data from a given base address. Max length is 14
275    ReadFloats {
276        addr: u16,
277        len: u8,
278    },
279
280    /// 0x04: Writes byte data to the given address
281    WriteMemory {
282        addr: u16,
283        data: BytesWrap,
284    },
285
286    /// 0x05: Reads byte data from the given address. Max read sizes are 61 bytes. (64 - crc - len - cmd)
287    ReadMemory {
288        addr: u16,
289        size: u8,
290    },
291
292    /// 0x25: Sets the current configuration
293    SetConfig {
294        config: u8,
295        reset: bool,
296    },
297
298    /// 0x34: Unary command to set the current source
299    SetSource {
300        source: u8,
301    },
302
303    /// 0x17 Unary command to set the master mute setting
304    SetMute {
305        value: bool,
306    },
307
308    /// 0x42: Set master volume
309    SetVolume {
310        value: Gain,
311    },
312
313    /// 0x30: Write biquad data
314    WriteBiquad {
315        addr: Addr,
316        data: [Value; 5],
317    },
318
319    /// 0x19: Toggle biquad filter bypass
320    WriteBiquadBypass {
321        addr: Addr,
322        value: bool,
323    },
324
325    /// 0x02: Read DSP data
326    Read {
327        addr: Addr,
328        len: u8,
329    },
330
331    /// 0x13: Write dsp data
332    Write {
333        addr: Addr,
334        value: Value,
335    },
336
337    /// 0x39: Start FIR load
338    FirLoadStart {
339        index: u8,
340    },
341
342    /// 0x3a: FIR Data
343    FirLoadData {
344        index: u8,
345        data: Vec<f32>, // Max 15 floats
346    },
347
348    /// 0x3b: FIR Data Completed
349    FirLoadEnd,
350
351    // 0x3f: DIRAC bypass
352    DiracBypass {
353        value: u8,
354    },
355
356    // Speculative commands
357    /// 0x12: Seen when restoring a configuration
358    BulkLoad {
359        // Initial payload:
360        // 04 88 97 13 0f 00 00
361        // 04: 4 | (Addr&0x0F0000 >> 12)
362        // 88: (Addr&0xFF00 >> 8)
363        // 97: (Addr&0xFF)
364        // 13: constant
365        // 0f: constant
366        // 00: constant
367        // 00: constant
368        payload: BytesWrap,
369    },
370
371    /// 0x06: Seen after 0x12 in configuration restore
372    BulkLoadFilterData {
373        // Initial packet:
374        // 02 05 (addr+3 u16)
375        payload: BytesWrap,
376    },
377
378    // 0x07: Seen in configuration restore
379    Unk07 {
380        payload: u8,
381    },
382
383    SwitchMux {
384        addr: Addr, //  start addr
385        max: u8,    // mux max
386        arg: bool,  // muxmax |= 0x80 if arg
387        slot: u8,   // slot
388    },
389
390    Unknown {
391        cmd_id: u8,
392        payload: BytesWrap,
393    },
394}
395
396impl Commands {
397    pub fn from_bytes(mut frame: Bytes) -> Result<Commands, ProtocolError> {
398        Ok(match frame.try_get_u8()? {
399            0x02 => Commands::Read {
400                addr: {
401                    let len = if frame.len() >= 3 { 3 } else { 2 };
402                    Addr::read(&mut frame, len)?
403                },
404                len: frame.try_get_u8()?,
405            },
406            0x04 => Commands::WriteMemory {
407                addr: frame.try_get_u16()?,
408                data: BytesWrap(frame),
409            },
410            0x05 => Commands::ReadMemory {
411                addr: frame.try_get_u16()?,
412                size: frame.try_get_u8()?,
413            },
414            0x06 => Commands::BulkLoadFilterData {
415                payload: BytesWrap(frame),
416            },
417            0x07 => Commands::Unk07 {
418                payload: frame.try_get_u8()?,
419            },
420            0x12 => Commands::BulkLoad {
421                payload: BytesWrap(frame),
422            },
423            0x13 => {
424                let len = if frame.len() >= 7 { 3 } else { 2 };
425                Commands::Write {
426                    addr: Addr::read(&mut frame, len)?,
427                    value: Value::from_bytes(frame)?,
428                }
429            }
430            0x14 => Commands::ReadFloats {
431                addr: frame.try_get_u16()?,
432                len: frame.try_get_u8()?,
433            },
434            0x17 => Commands::SetMute {
435                value: frame.try_get_u8()? != 0,
436            },
437            0x19 => {
438                let len = if frame.len() > 2 { 3 } else { 2 };
439                let addr = Addr::read(&mut frame, len)?;
440
441                Commands::WriteBiquadBypass {
442                    value: addr.extra_bit,
443                    addr,
444                }
445            }
446            0x25 => Commands::SetConfig {
447                config: frame.try_get_u8()?,
448                reset: frame.try_get_u8()? != 0,
449            },
450            0x31 => Commands::ReadHardwareId {},
451            0x29 => {
452                let addr = Addr::read(&mut frame, 2)?;
453                let mut max = frame.try_get_u8()?;
454                let arg = max & 0x80 != 0;
455                if arg {
456                    max ^= 0x80;
457                }
458                let slot = frame.try_get_u8()?;
459
460                Commands::SwitchMux {
461                    addr,
462                    max,
463                    arg,
464                    slot,
465                }
466            }
467            0x30 => Commands::WriteBiquad {
468                addr: {
469                    let len = if frame.len() > 24 { 3 } else { 2 };
470                    Addr::read(&mut frame, len)?
471                },
472                data: {
473                    frame.try_get_u16()?;
474                    let mut data: [Value; 5] = Default::default();
475                    for f in data.iter_mut() {
476                        *f = Value::Unknown(Bytes::copy_from_slice(&frame[0..4]));
477                        frame.advance(4);
478                    }
479                    data
480                },
481            },
482            0x34 => Commands::SetSource {
483                source: frame.try_get_u8()?,
484            },
485            0x39 => Commands::FirLoadStart {
486                index: frame.try_get_u8()?,
487            },
488            0x3a => Commands::FirLoadData {
489                index: frame.try_get_u8()?,
490                data: {
491                    let mut data = Vec::with_capacity(15);
492                    while frame.len() > 4 {
493                        data.push(frame.try_get_f32_le()?);
494                    }
495                    data
496                },
497            },
498            0x3b => Commands::FirLoadEnd,
499            0x42 => Commands::SetVolume {
500                value: frame.try_get_u8()?.into(),
501            },
502            cmd_id => Commands::Unknown {
503                cmd_id,
504                payload: BytesWrap(frame),
505            },
506        })
507    }
508
509    pub fn to_bytes(&self) -> Bytes {
510        let mut f = BytesMut::with_capacity(64);
511
512        match self {
513            Commands::ReadHardwareId => {
514                f.put_u8(0x31);
515            }
516            &Commands::ReadFloats { addr, len } => {
517                f.put_u8(0x14);
518                f.put_u16(addr);
519                f.put_u8(len);
520            }
521            &Commands::ReadMemory { addr, size } => {
522                f.put_u8(0x05);
523                f.put_u16(addr);
524                f.put_u8(size);
525            }
526            &Commands::WriteMemory { addr, ref data } => {
527                f.put_u8(0x04);
528                f.put_u16(addr);
529                f.put(data.0.clone());
530            }
531            &Commands::SetConfig { config, reset } => {
532                f.put_u8(0x25);
533                f.put_u8(config);
534                f.put_u8(reset as u8);
535            }
536            &Commands::SetSource { source } => {
537                f.put_u8(0x34);
538                f.put_u8(source);
539            }
540            &Commands::SetMute { value } => {
541                f.put_u8(0x17);
542                f.put_u8(value as u8);
543            }
544            &Commands::SetVolume { value } => {
545                f.put_u8(0x42);
546                f.put_u8((value).into());
547            }
548            Commands::WriteBiquad { addr, data } => {
549                f.put_u8(0x30);
550                addr.write(&mut f);
551                f.put_u16(0x0000);
552                for coeff in data.iter() {
553                    f.put(coeff.clone().into_bytes());
554                }
555            }
556            &Commands::WriteBiquadBypass { mut addr, value } => {
557                f.put_u8(0x19);
558                addr.extra_bit = value;
559                addr.write(&mut f);
560            }
561            &Commands::Read { addr, len } => {
562                f.put_u8(0x2);
563                addr.write(&mut f);
564                f.put_u8(len);
565            }
566            &Commands::Write { addr, ref value } => {
567                f.put_u8(0x13);
568                addr.write(&mut f);
569                f.put(value.clone().into_bytes());
570            }
571
572            &Commands::FirLoadStart { index } => {
573                f.put_u8(0x39);
574                f.put_u8(index);
575            }
576            &Commands::FirLoadData { index, ref data } => {
577                f.put_u8(0x3a);
578                f.put_u8(index);
579                for &coeff in data {
580                    f.put_f32_le(coeff);
581                }
582            }
583            &Commands::FirLoadEnd => {
584                f.put_u8(0x3b);
585            }
586            Commands::BulkLoad { payload } => {
587                f.put_u8(0x12);
588                f.put(payload.0.clone());
589            }
590            Commands::BulkLoadFilterData { payload } => {
591                f.put_u8(0x06);
592                f.put(payload.0.clone());
593            }
594            &Commands::DiracBypass { value } => {
595                f.put_u8(0x3f);
596                f.put_u8(value);
597            }
598            &Commands::SwitchMux {
599                addr,
600                mut max,
601                arg,
602                slot,
603            } => {
604                f.put_u8(0x29);
605                addr.write(&mut f);
606                if arg {
607                    max |= 0x80;
608                }
609                f.put_u8(max);
610                f.put_u8(slot);
611            }
612            &Commands::Unk07 { payload } => {
613                f.put_u8(0x07);
614                f.put_u8(payload);
615            }
616            &Commands::Unknown {
617                cmd_id,
618                ref payload,
619            } => {
620                f.put_u8(cmd_id);
621                f.put(payload.0.clone());
622            }
623        }
624        f.freeze()
625    }
626
627    pub fn matches_response(&self, response: &Responses) -> bool {
628        match self {
629            &Commands::ReadMemory { addr, size } => {
630                if let Responses::MemoryData(data) = response {
631                    data.base == addr && data.data.len() == size as usize
632                } else {
633                    false
634                }
635            }
636            &Commands::ReadFloats { addr, len } => {
637                if let Responses::FloatData(data) = response {
638                    data.base == addr && data.data.len() == len as usize
639                } else {
640                    false
641                }
642            }
643            Commands::ReadHardwareId => matches!(response, Responses::HardwareId { .. }),
644            Commands::SetConfig { .. } => matches!(response, Responses::ConfigChanged),
645            Commands::FirLoadStart { .. } => matches!(response, Responses::FirLoadSize { .. }),
646            &Commands::Unk07 { .. } => matches!(response, Responses::Unk02 { .. }),
647            &Commands::Read { .. } => matches!(response, Responses::Read { .. }),
648            Commands::WriteMemory { .. }
649            | Commands::SetSource { .. }
650            | Commands::SetMute { .. }
651            | Commands::SetVolume { .. }
652            | Commands::WriteBiquad { .. }
653            | Commands::WriteBiquadBypass { .. }
654            | Commands::Write { .. }
655            | Commands::FirLoadData { .. }
656            | Commands::FirLoadEnd
657            | Commands::BulkLoad { .. }
658            | Commands::BulkLoadFilterData { .. }
659            | Commands::DiracBypass { .. }
660            | Commands::SwitchMux { .. } => matches!(response, Responses::Ack),
661            Commands::Unknown { .. } => true,
662        }
663    }
664
665    pub fn mute(addr: Addr, value: bool) -> Self {
666        let value: u16 = if value {
667            WriteInt::DISABLED
668        } else {
669            WriteInt::ENABLED
670        };
671
672        Commands::Write {
673            addr,
674            value: Value::Int(value),
675        }
676    }
677}
678
679#[derive(Clone)]
680#[cfg_attr(feature = "debug", derive(Debug))]
681pub enum Responses {
682    Ack,
683    Read {
684        addr: Addr,
685        data: Vec<Value>,
686    },
687    MemoryData(MemoryView),
688    FloatData(FloatView),
689    HardwareId {
690        payload: BytesWrap,
691    },
692    FirLoadSize {
693        size: u16,
694    },
695
696    /// Speculative commands
697    ConfigChanged,
698
699    // 0x02: Seen during configuration restore as a response to [`Commands::Unk07`]
700    Unk02,
701
702    Unknown {
703        cmd_id: u8,
704        payload: BytesWrap,
705    },
706}
707
708impl Responses {
709    pub fn from_bytes(mut frame: Bytes) -> Result<Responses, ProtocolError> {
710        if frame.is_empty() {
711            return Ok(Responses::Ack);
712        }
713
714        Ok(match frame[0] {
715            0x02 => Responses::Read {
716                addr: {
717                    frame.try_get_u8()?;
718                    Addr::read(&mut frame, 2)?
719                },
720                data: {
721                    frame
722                        .chunks(4)
723                        .filter_map(|x| Value::from_bytes(Bytes::copy_from_slice(x)).ok())
724                        .collect()
725                },
726            },
727            0x05 => Responses::MemoryData(MemoryView::from_packet(frame)?),
728            0x14 => Responses::FloatData(FloatView::from_packet(frame)?),
729            0x31 => Responses::HardwareId {
730                payload: {
731                    frame.try_get_u8()?;
732                    BytesWrap(frame)
733                },
734            },
735            0x39 => Responses::FirLoadSize {
736                size: {
737                    frame.try_get_u8()?; // Consume command id
738                    frame.try_get_u16()?
739                },
740            },
741            0xab => Responses::ConfigChanged,
742            cmd_id => Responses::Unknown {
743                cmd_id,
744                payload: BytesWrap(frame),
745            },
746        })
747    }
748
749    pub fn to_bytes(&self) -> Bytes {
750        let mut f = BytesMut::with_capacity(64);
751        match self {
752            Responses::Ack => {}
753            Responses::MemoryData(data) => {
754                f.put_u8(0x05);
755                f.put_u16(data.base);
756                f.put(data.data.clone());
757            }
758            Responses::FloatData(data) => {
759                f.put_u8(0x14);
760                f.put_u16(data.base);
761
762                for &item in &data.data {
763                    f.put_f32_le(item);
764                }
765            }
766            &Responses::Unknown {
767                cmd_id,
768                ref payload,
769            } => {
770                f.put_u8(cmd_id);
771                f.put(payload.0.clone());
772            }
773            Responses::HardwareId { payload } => {
774                f.put_u8(0x31);
775                f.put(payload.0.clone());
776            }
777            &Responses::FirLoadSize { size } => {
778                f.put_u8(0x39);
779                f.put_u16(size);
780            }
781            Responses::ConfigChanged => {
782                f.put_u8(0xab);
783            }
784            Responses::Read { addr, data } => {
785                f.put_u8(0x02);
786                addr.write(&mut f);
787                for d in data {
788                    f.put(d.clone().into_bytes())
789                }
790            }
791            &Responses::Unk02 => {
792                f.put_u8(0x02);
793            }
794        }
795        f.freeze()
796    }
797
798    pub fn is_memory_view(&self) -> bool {
799        matches!(self, Responses::MemoryData(_))
800    }
801
802    pub fn into_memory_view(self) -> Result<MemoryView, ProtocolError> {
803        match self {
804            Responses::MemoryData(m) => Ok(m),
805            _ => Err(ProtocolError::UnexpectedResponseType),
806        }
807    }
808
809    pub fn is_float_view(&self) -> bool {
810        matches!(self, Responses::FloatData(_))
811    }
812
813    pub fn into_float_view(self) -> Result<FloatView, ProtocolError> {
814        match self {
815            Responses::FloatData(m) => Ok(m),
816            _ => Err(ProtocolError::UnexpectedResponseType),
817        }
818    }
819
820    pub fn is_hardware_id(&self) -> bool {
821        matches!(self, Responses::HardwareId { .. })
822    }
823
824    pub fn into_hardware_id(self) -> Result<u8, ProtocolError> {
825        match self {
826            Responses::HardwareId { payload } => {
827                Ok(*payload.last().ok_or(ProtocolError::MalformedHardwareId)?)
828            }
829            _ => Err(ProtocolError::UnexpectedResponseType),
830        }
831    }
832
833    pub fn is_ack(&self) -> bool {
834        matches!(self, Responses::Ack)
835    }
836
837    pub fn into_ack(self) -> Result<(), ProtocolError> {
838        match self {
839            Responses::Ack => Ok(()),
840            _ => Err(ProtocolError::UnexpectedResponseType),
841        }
842    }
843
844    pub fn is_config_changed(&self) -> bool {
845        matches!(self, Responses::ConfigChanged)
846    }
847
848    pub fn into_config_changed(self) -> Result<(), ProtocolError> {
849        match self {
850            Responses::ConfigChanged => Ok(()),
851            _ => Err(ProtocolError::UnexpectedResponseType),
852        }
853    }
854
855    pub fn is_fir_size(&self) -> bool {
856        matches!(self, Responses::FirLoadSize { .. })
857    }
858
859    pub fn into_fir_size(self) -> Result<u16, ProtocolError> {
860        match self {
861            Responses::FirLoadSize { size } => Ok(size),
862            _ => Err(ProtocolError::UnexpectedResponseType),
863        }
864    }
865}
866
867/// Parsable response type
868pub trait UnaryResponse: Sized {
869    fn from_packet(packet: Bytes) -> Result<Self, ProtocolError>;
870}
871
872#[derive(Copy, Clone, PartialEq, Default)]
873#[cfg_attr(feature = "debug", derive(Debug))]
874#[cfg_attr(
875    feature = "serde",
876    derive(Serialize, Deserialize, schemars::JsonSchema)
877)]
878/// A gain between the minimum and maximum allowed values
879pub struct Gain(pub f32);
880
881impl Gain {
882    pub const MIN: f32 = -127.;
883    pub const MAX: f32 = 0.;
884}
885
886impl From<Gain> for u8 {
887    fn from(val: Gain) -> Self {
888        (val.0.abs() * 2.) as u8
889    }
890}
891
892impl From<u8> for Gain {
893    fn from(val: u8) -> Self {
894        Self(-0.5 * (val as f32))
895    }
896}
897
898impl FromStr for Gain {
899    type Err = <f32 as FromStr>::Err;
900
901    fn from_str(s: &str) -> Result<Self, Self::Err> {
902        Ok(Gain(<f32 as FromStr>::from_str(s)?))
903    }
904}
905/// Memory views can be extended with multiple contiguous reads
906pub trait ExtendView {
907    fn extend_with(&mut self, other: Self) -> Result<(), ExtendError>;
908}
909
910#[cfg_attr(feature = "debug", derive(Debug, Error))]
911pub enum ExtendError {
912    #[cfg_attr(feature = "debug", error("the corresponding bases do not align"))]
913    MismatchingBases,
914}
915
916/// A contiguous view of floats read from the device
917#[derive(Clone, Default)]
918#[cfg_attr(feature = "debug", derive(Debug))]
919pub struct FloatView {
920    pub base: u16,
921    pub data: Vec<f32>,
922}
923
924impl FloatView {
925    pub fn get(&self, addr: u16) -> f32 {
926        self.data[(addr - self.base) as usize]
927    }
928}
929
930impl ExtendView for FloatView {
931    fn extend_with(&mut self, other: Self) -> Result<(), ExtendError> {
932        // Check that the `other` starts a the end of `self`
933        let expected_start = self.base + (self.data.len() as u16);
934        if other.base != expected_start {
935            return Err(ExtendError::MismatchingBases);
936        }
937
938        self.data.extend(other.data.iter());
939
940        Ok(())
941    }
942}
943
944impl UnaryResponse for FloatView {
945    fn from_packet(mut packet: Bytes) -> Result<Self, ProtocolError> {
946        packet.try_get_u8()?; // Discard command id 0x14
947        let base = packet.try_get_u16()?;
948        let data = packet
949            .chunks_exact(4)
950            .map(|x| x.try_into().unwrap())
951            .map(f32::from_le_bytes)
952            .collect();
953
954        Ok(FloatView { base, data })
955    }
956}
957
958#[derive(Clone, Default)]
959pub struct MemoryView {
960    pub base: u16,
961    pub data: Bytes,
962}
963
964impl MemoryView {
965    pub fn read_at(&self, addr: u16, len: u8) -> Option<&'_ [u8]> {
966        if addr < self.base || addr as usize > self.base as usize + self.data.len() {
967            return None;
968        }
969
970        let start = (addr - self.base) as usize;
971        let end = start + len as usize;
972
973        if self.data.len() < end {
974            return None;
975        }
976
977        Some(&self.data[start..end])
978    }
979
980    pub fn read_u8(&self, addr: u16) -> Option<u8> {
981        Some(self.read_at(addr, 1)?[0])
982    }
983
984    pub fn read_u16(&self, addr: u16) -> Option<u16> {
985        Some(u16::from_be_bytes(
986            self.read_at(addr, 2)?.try_into().unwrap(),
987        ))
988    }
989}
990
991impl UnaryResponse for MemoryView {
992    fn from_packet(mut packet: Bytes) -> Result<Self, ProtocolError> {
993        packet.try_get_u8()?; // Discard command id 0x5
994        let base = packet.try_get_u16()?;
995
996        Ok(MemoryView { base, data: packet })
997    }
998}
999
1000#[cfg(feature = "debug")]
1001impl fmt::Debug for MemoryView {
1002    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1003        write!(
1004            f,
1005            "MemoryView {{ base: {:04x?}, data: {:02x?} }}",
1006            self.base,
1007            self.data.as_ref()
1008        )
1009    }
1010}
1011
1012impl ExtendView for MemoryView {
1013    fn extend_with(&mut self, other: Self) -> Result<(), ExtendError> {
1014        // Check that the `other` starts a the end of `self`
1015        let expected_start = self.base + (self.data.len() as u16);
1016        if other.base != expected_start {
1017            return Err(ExtendError::MismatchingBases);
1018        }
1019
1020        let mut data: BytesMut = BytesMut::with_capacity(self.data.len() + other.data.len());
1021        data.extend(self.data.iter());
1022        data.extend(other.data.iter());
1023
1024        // Truncate anything past 0xFFFF since it's probably garbage
1025        data.truncate((u16::MAX as usize) - (self.base as usize));
1026
1027        self.data = data.freeze();
1028
1029        Ok(())
1030    }
1031}
1032
1033/// 0x13: Write an integer value
1034#[derive(Clone, Default)]
1035#[cfg_attr(feature = "debug", derive(Debug))]
1036pub struct WriteInt;
1037
1038impl WriteInt {
1039    pub const DISABLED: u16 = 1;
1040    pub const ENABLED: u16 = 2;
1041    pub const BYPASSED: u16 = 3;
1042}
1043
1044/// Types that can be read from a contiguous memory representation
1045pub trait FromMemory<T: Sized>
1046where
1047    Self: Sized,
1048{
1049    fn from_memory(device_info: &DeviceInfo, view: &MemoryView) -> Result<Self>;
1050}
1051
1052impl FromMemory<MasterStatus> for MasterStatus
1053where
1054    Self: Sized,
1055{
1056    fn from_memory(device_info: &DeviceInfo, view: &MemoryView) -> Result<Self> {
1057        Ok(Self {
1058            preset: view.read_u8(eeprom::PRESET),
1059            source: view
1060                .read_u8(eeprom::SOURCE)
1061                .or_else(|| view.read_u8(eeprom::SOURCE_ASYNC))
1062                .map(|id| super::Source::from_id(id, device_info)),
1063            volume: view.read_u8(eeprom::MASTER_VOLUME).map(Into::into),
1064            mute: view.read_u8(eeprom::MUTE).map(|x| x == 1),
1065            dirac: if device_info.supports_dirac() {
1066                view.read_u8(eeprom::DIRAC_BYPASS).map(|x| x == 0)
1067            } else {
1068                None
1069            },
1070        })
1071    }
1072}
1073
1074#[cfg(test)]
1075mod test {
1076    use super::*;
1077
1078    #[test]
1079    fn test_addr() {
1080        let tests: &[(&[u8], Addr)] = &[
1081            // Should trim the prefixed 0x80
1082            (
1083                &[0x80, 0x10],
1084                Addr {
1085                    extra_bit: true,
1086                    val: 0x10,
1087                    len: 2,
1088                },
1089            ),
1090            // Should trim the prefixed 0x80
1091            (
1092                &[0x80, 0x10, 0x00],
1093                Addr {
1094                    extra_bit: true,
1095                    val: 0x10_00,
1096                    len: 3,
1097                },
1098            ),
1099            (
1100                &[0x8F, 0x10],
1101                Addr {
1102                    extra_bit: true,
1103                    val: 0xF10,
1104                    len: 2,
1105                },
1106            ),
1107            (
1108                &[0x8F, 0x10, 0x00],
1109                Addr {
1110                    extra_bit: true,
1111                    val: 0x0F_10_00,
1112                    len: 3,
1113                },
1114            ),
1115        ];
1116
1117        for (bytes, addr) in tests {
1118            let data = Bytes::from_static(bytes);
1119            let parsed = Addr::read(&mut data.clone(), data.len() as _).unwrap();
1120            assert_eq!(addr, &parsed);
1121
1122            let mut written_bytes = BytesMut::new();
1123            addr.write(&mut written_bytes);
1124            assert_eq!(&written_bytes, bytes);
1125        }
1126    }
1127
1128    #[test]
1129    fn test_read_reg() {
1130        let cmd = Commands::ReadMemory {
1131            addr: 0xffda,
1132            size: 4,
1133        };
1134
1135        let mut req_packet = cmd.to_bytes();
1136        assert_eq!(req_packet.get_u8(), 0x05);
1137        assert_eq!(req_packet.get_u16(), 0xffda);
1138        assert_eq!(req_packet.get_u8(), 4);
1139        assert_eq!(req_packet.remaining(), 0);
1140
1141        let response = Bytes::from_static(&[0x5, 0xff, 0xda, 0x1, 0x2, 0x3, 0x4, 0x0]);
1142        let memory = Responses::from_bytes(response)
1143            .ok()
1144            .unwrap()
1145            .into_memory_view()
1146            .ok()
1147            .unwrap();
1148        let data = memory.read_at(0xffda, 4);
1149
1150        assert_eq!(data.unwrap(), &[0x1, 0x2, 0x3, 0x4]);
1151        assert_eq!(memory.read_u16(0xFFDA), Some(0x0102));
1152    }
1153
1154    #[test]
1155    fn test_master_status() {
1156        let cmd = Commands::ReadMemory {
1157            addr: 0xffd8,
1158            size: 9,
1159        };
1160
1161        let mut req_packet = cmd.to_bytes();
1162        assert_eq!(req_packet.get_u8(), 0x05);
1163        assert_eq!(req_packet.get_u16(), 0xffd8);
1164        assert_eq!(req_packet.get_u8(), 9);
1165        assert_eq!(req_packet.remaining(), 0);
1166
1167        let response = Bytes::from_static(&[
1168            0x5, 0xff, 0xd8, 0x0, 0x1, 0x4f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1169        ]);
1170        let memory = Responses::from_bytes(response)
1171            .ok()
1172            .unwrap()
1173            .into_memory_view()
1174            .ok()
1175            .unwrap();
1176
1177        let device_info = DeviceInfo {
1178            hw_id: 10,
1179            dsp_version: 100,
1180            serial: 0,
1181        };
1182        let status = MasterStatus::from_memory(&device_info, &memory).unwrap();
1183        assert!(status.eq(&MasterStatus {
1184            preset: Some(0),
1185            source: Some(crate::Source::Toslink),
1186            volume: Some(Gain(-39.5)),
1187            mute: Some(false),
1188            dirac: None,
1189        }));
1190    }
1191
1192    #[test]
1193    fn test_combine() {
1194        let mut f1 = FloatView {
1195            base: 0,
1196            data: (0u16..10).map(|x| x.into()).collect(),
1197        };
1198
1199        let f2 = FloatView {
1200            base: 10,
1201            data: (10u16..20).map(|x| x.into()).collect(),
1202        };
1203
1204        f1.extend_with(f2).ok().unwrap();
1205        assert_eq!(f1.base, 0);
1206        assert_eq!(f1.data.len(), 20);
1207        assert!(f1
1208            .data
1209            .into_iter()
1210            .eq((0u16..20).into_iter().map(|x| -> f32 { x.into() })));
1211
1212        let mut m1 = MemoryView {
1213            base: 0,
1214            data: (0u8..10).collect(),
1215        };
1216
1217        let m2 = MemoryView {
1218            base: 10,
1219            data: (10u8..20).collect(),
1220        };
1221
1222        m1.extend_with(m2).ok().unwrap();
1223        assert_eq!(m1.base, 0);
1224        assert_eq!(m1.data.len(), 20);
1225        assert!(m1.data.into_iter().eq((0u8..20).into_iter()));
1226    }
1227}