firewire_fireworks_protocols/
flash.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol about operations for on-board flash memory.
5//!
6//! The module includes protocol about operations for on-board flash memory defined by Echo Audio
7//! Digital Corporation for Fireworks board module.
8
9use super::*;
10
11const CATEGORY_FLASH: u32 = 1;
12
13const CMD_ERASE: u32 = 0;
14const CMD_READ: u32 = 1;
15const CMD_WRITE: u32 = 2;
16const CMD_STATUS: u32 = 3;
17const CMD_SESSION_BASE: u32 = 4;
18const CMD_LOCK: u32 = 5;
19
20/// The size of block in on-board flash memory in quadlet unit.
21pub const BLOCK_QUADLET_COUNT: usize = 64;
22
23/// The parameter to erase content of flash for a block.
24#[derive(Default, Debug, Clone, PartialEq, Eq)]
25pub struct EfwFlashErase {
26    /// The offset in flash memory. It should be aligned by quadlet.
27    pub offset: u32,
28}
29
30impl<O, P> EfwWhollyUpdatableParamsOperation<P, EfwFlashErase> for O
31where
32    O: EfwHardwareSpecification,
33    P: EfwProtocolExtManual,
34{
35    fn update_wholly(proto: &mut P, states: &EfwFlashErase, timeout_ms: u32) -> Result<(), Error> {
36        assert_eq!(states.offset % 4, 0);
37
38        let args = [states.offset];
39        let mut params = Vec::new();
40        proto.transaction(CATEGORY_FLASH, CMD_ERASE, &args, &mut params, timeout_ms)
41    }
42}
43
44/// The parameter to erase content of flash for a block.
45#[derive(Default, Debug, Clone, PartialEq, Eq)]
46pub struct EfwFlashRead {
47    /// The offset in flash memory. It should be aligned by quadlet.
48    pub offset: u32,
49    /// The content. The length should be less than 64.
50    pub data: Vec<u32>,
51}
52
53impl<O, P> EfwWhollyCachableParamsOperation<P, EfwFlashRead> for O
54where
55    O: EfwHardwareSpecification,
56    P: EfwProtocolExtManual,
57{
58    fn cache_wholly(
59        proto: &mut P,
60        states: &mut EfwFlashRead,
61        timeout_ms: u32,
62    ) -> Result<(), Error> {
63        assert_eq!(states.offset % 4, 0);
64        assert!(states.data.len() <= BLOCK_QUADLET_COUNT);
65
66        let count = states.data.len();
67        let args = [states.offset, count as u32];
68        let mut params = vec![0; 2 + BLOCK_QUADLET_COUNT];
69
70        proto
71            .transaction(CATEGORY_FLASH, CMD_READ, &args, &mut params, timeout_ms)
72            .map(|_| states.data.copy_from_slice(&params[2..(2 + count)]))
73    }
74}
75
76/// The parameter to write content of flash.
77#[derive(Default, Debug, Clone, PartialEq, Eq)]
78pub struct EfwFlashWrite {
79    /// The offset in flash memory. It should be aligned by quadlet.
80    pub offset: u32,
81    /// The content. The length should be less than 64.
82    pub data: Vec<u32>,
83}
84
85impl<O, P> EfwWhollyUpdatableParamsOperation<P, EfwFlashWrite> for O
86where
87    O: EfwHardwareSpecification,
88    P: EfwProtocolExtManual,
89{
90    fn update_wholly(proto: &mut P, states: &EfwFlashWrite, timeout_ms: u32) -> Result<(), Error> {
91        assert_eq!(states.offset % 4, 0);
92        assert!(states.data.len() <= BLOCK_QUADLET_COUNT);
93
94        let mut args = vec![0; 2 + BLOCK_QUADLET_COUNT];
95        args[0] = states.offset;
96        args[1] = states.data.len() as u32;
97        args[2..(2 + states.data.len())].copy_from_slice(&states.data);
98
99        let mut params = Vec::new();
100
101        proto.transaction(CATEGORY_FLASH, CMD_WRITE, &args, &mut params, timeout_ms)
102    }
103}
104
105/// The parameter to check whether the flash memory is locked or not.
106#[derive(Debug, Copy, Clone, PartialEq, Eq)]
107pub enum EfwFlashState {
108    /// Is unlocked.
109    Unlocked,
110    /// Is locked.
111    Locked,
112}
113
114impl Default for EfwFlashState {
115    fn default() -> Self {
116        Self::Unlocked
117    }
118}
119
120impl<O, P> EfwWhollyCachableParamsOperation<P, EfwFlashState> for O
121where
122    O: EfwHardwareSpecification,
123    P: EfwProtocolExtManual,
124{
125    fn cache_wholly(
126        proto: &mut P,
127        states: &mut EfwFlashState,
128        timeout_ms: u32,
129    ) -> Result<(), Error> {
130        let args = Vec::new();
131        let mut params = Vec::new();
132        proto
133            .transaction(CATEGORY_FLASH, CMD_STATUS, &args, &mut params, timeout_ms)
134            .map(|_| *states = EfwFlashState::Unlocked)
135            .or_else(|e| {
136                if e.kind::<EfwProtocolError>() == Some(EfwProtocolError::FlashBusy) {
137                    *states = EfwFlashState::Locked;
138                    Ok(())
139                } else {
140                    Err(e)
141                }
142            })
143    }
144}
145
146impl<O, P> EfwWhollyUpdatableParamsOperation<P, EfwFlashState> for O
147where
148    O: EfwHardwareSpecification,
149    P: EfwProtocolExtManual,
150{
151    fn update_wholly(proto: &mut P, states: &EfwFlashState, timeout_ms: u32) -> Result<(), Error> {
152        let args = vec![states.eq(&EfwFlashState::Locked) as u32];
153        let mut params = Vec::new();
154        proto.transaction(CATEGORY_FLASH, CMD_LOCK, &args, &mut params, timeout_ms)
155    }
156}
157
158/// The parameter for session base in flash memory.
159#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
160pub struct EfwFlashSessionBase(pub u32);
161
162impl<O, P> EfwWhollyCachableParamsOperation<P, EfwFlashSessionBase> for O
163where
164    O: EfwHardwareSpecification,
165    P: EfwProtocolExtManual,
166{
167    fn cache_wholly(
168        proto: &mut P,
169        states: &mut EfwFlashSessionBase,
170        timeout_ms: u32,
171    ) -> Result<(), Error> {
172        let args = Vec::new();
173        let mut params = vec![0];
174        proto
175            .transaction(
176                CATEGORY_FLASH,
177                CMD_SESSION_BASE,
178                &args,
179                &mut params,
180                timeout_ms,
181            )
182            .map(|_| states.0 = params[0])
183    }
184}
185
186#[cfg(test)]
187mod test {
188    use super::*;
189    use glib::{translate::FromGlib, SignalHandlerId};
190    use std::cell::RefCell;
191
192    const BLOCK_SIZE: usize = 4 * BLOCK_QUADLET_COUNT as usize;
193    const TIMEOUT: u32 = 10;
194
195    struct TestProtocol;
196
197    impl EfwHardwareSpecification for TestProtocol {
198        const SUPPORTED_SAMPLING_RATES: &'static [u32] = &[];
199        const SUPPORTED_SAMPLING_CLOCKS: &'static [ClkSrc] = &[];
200        const CAPABILITIES: &'static [HwCap] = &[];
201        const RX_CHANNEL_COUNTS: [usize; 3] = [0; 3];
202        const TX_CHANNEL_COUNTS: [usize; 3] = [0; 3];
203        const MONITOR_SOURCE_COUNT: usize = 0;
204        const MONITOR_DESTINATION_COUNT: usize = 0;
205        const MIDI_INPUT_COUNT: usize = 0;
206        const MIDI_OUTPUT_COUNT: usize = 0;
207        const PHYS_INPUT_GROUPS: &'static [(PhysGroupType, usize)] = &[];
208        const PHYS_OUTPUT_GROUPS: &'static [(PhysGroupType, usize)] = &[];
209    }
210
211    #[derive(Default)]
212    struct TestInstance(RefCell<StateMachine>);
213
214    #[test]
215    fn flash_lock_test() {
216        let mut proto = TestInstance::default();
217
218        // The initial status should be locked.
219        let mut state = EfwFlashState::default();
220        TestProtocol::cache_wholly(&mut proto, &mut state, TIMEOUT).unwrap();
221
222        // The erase operation should be failed due to locked status.
223        let erase = EfwFlashErase { offset: 256 };
224        let err = TestProtocol::update_wholly(&mut proto, &erase, TIMEOUT).unwrap_err();
225        assert_eq!(
226            err.kind::<EfwProtocolError>(),
227            Some(EfwProtocolError::FlashBusy)
228        );
229
230        // The write operation should be failed as well due to locked status.
231        let write = EfwFlashWrite {
232            offset: 256,
233            data: vec![0; 16],
234        };
235        let err = TestProtocol::update_wholly(&mut proto, &write, TIMEOUT).unwrap_err();
236        assert_eq!(
237            err.kind::<EfwProtocolError>(),
238            Some(EfwProtocolError::FlashBusy)
239        );
240
241        // The read operation is always available.
242        let mut read = EfwFlashRead {
243            offset: 0,
244            data: vec![0; 64],
245        };
246        TestProtocol::cache_wholly(&mut proto, &mut read, TIMEOUT).unwrap();
247
248        // Unlock it.
249        let state = EfwFlashState::Unlocked;
250        TestProtocol::update_wholly(&mut proto, &state, TIMEOUT).unwrap();
251
252        // The erase operation should be available now.
253        TestProtocol::update_wholly(&mut proto, &erase, TIMEOUT).unwrap();
254
255        // The write operation should be available now.
256        TestProtocol::update_wholly(&mut proto, &write, TIMEOUT).unwrap();
257
258        // The read operation is always available.
259        TestProtocol::cache_wholly(&mut proto, &mut read, TIMEOUT).unwrap();
260
261        // Lock it.
262        let state = EfwFlashState::Locked;
263        TestProtocol::update_wholly(&mut proto, &state, TIMEOUT).unwrap();
264
265        let mut state = EfwFlashState::default();
266        TestProtocol::cache_wholly(&mut proto, &mut state, TIMEOUT).unwrap();
267        assert_eq!(state, EfwFlashState::Locked);
268
269        // The erase operation should be failed again;
270        let err = TestProtocol::update_wholly(&mut proto, &erase, TIMEOUT).unwrap_err();
271        assert_eq!(
272            err.kind::<EfwProtocolError>(),
273            Some(EfwProtocolError::FlashBusy)
274        );
275
276        // The write operation should be failed as well;
277        let err = TestProtocol::update_wholly(&mut proto, &write, TIMEOUT).unwrap_err();
278        assert_eq!(
279            err.kind::<EfwProtocolError>(),
280            Some(EfwProtocolError::FlashBusy)
281        );
282
283        // The read operation is always available.
284        TestProtocol::cache_wholly(&mut proto, &mut read, TIMEOUT).unwrap();
285    }
286
287    #[test]
288    fn flash_update_test() {
289        let mut proto = TestInstance::default();
290
291        let count = proto.0.borrow().memory.len() / 4;
292        (0..count).for_each(|i| {
293            let pos = i * 4;
294            proto.0.borrow_mut().memory[pos..(pos + 4)].copy_from_slice(&(i as u32).to_be_bytes());
295        });
296
297        let state = EfwFlashState::Unlocked;
298        TestProtocol::update_wholly(&mut proto, &state, TIMEOUT).unwrap();
299
300        let erase = EfwFlashErase { offset: 256 };
301        TestProtocol::update_wholly(&mut proto, &erase, TIMEOUT).unwrap();
302
303        // Check near the boundary between first and second blocks.
304        let mut read = EfwFlashRead {
305            offset: 248,
306            data: vec![0; 16],
307        };
308        TestProtocol::cache_wholly(&mut proto, &mut read, TIMEOUT).unwrap();
309
310        // Check near the boundary between second and third blocks.
311        let mut read = EfwFlashRead {
312            offset: 504,
313            data: vec![0; 8],
314        };
315        TestProtocol::cache_wholly(&mut proto, &mut read, TIMEOUT).unwrap();
316        assert_eq!(&read.data, &[0, 0, 128, 129, 130, 131, 132, 133]);
317
318        // Update the second block.
319        let data = (0..BLOCK_QUADLET_COUNT)
320            .map(|i| u32::MAX - i as u32)
321            .collect();
322        let write = EfwFlashWrite { offset: 256, data };
323        TestProtocol::update_wholly(&mut proto, &write, TIMEOUT).unwrap();
324
325        // Check near the boundary between second and third block.
326        let mut read = EfwFlashRead {
327            offset: 504,
328            data: vec![0; 6],
329        };
330        TestProtocol::cache_wholly(&mut proto, &mut read, TIMEOUT).unwrap();
331        assert_eq!(&read.data, &[4294967233, 4294967232, 128, 129, 130, 131]);
332    }
333
334    struct StateMachine {
335        // Here, the state machine is defined to have four blocks in which the first block is
336        // immutable.
337        memory: [u8; 4 * BLOCK_SIZE],
338        // At initial state, the memory is locked against erase and write operation.
339        locked: bool,
340    }
341
342    impl Default for StateMachine {
343        fn default() -> Self {
344            Self {
345                memory: [0; 4 * BLOCK_SIZE],
346                locked: true,
347            }
348        }
349    }
350
351    impl StateMachine {
352        fn erase_block(&mut self, args: &[u32], params: &mut Vec<u32>) -> Result<(), Error> {
353            if params.len() > 0 {
354                Err(Error::new(
355                    EfwProtocolError::BadParameter,
356                    "Useless parameter is given",
357                ))
358            } else if args.len() < 1 {
359                Err(Error::new(
360                    EfwProtocolError::BadCommand,
361                    "Argument is shorter than expected",
362                ))
363            } else {
364                // Align to block.
365                let pos = (args[0] as usize) / BLOCK_SIZE * BLOCK_SIZE;
366                if pos == 0 {
367                    Err(Error::new(
368                        EfwProtocolError::BadCommand,
369                        "The first block is immutable",
370                    ))
371                } else if pos > self.memory.len() {
372                    Err(Error::new(
373                        EfwProtocolError::BadCommand,
374                        "The offset is out of range",
375                    ))
376                } else if self.locked {
377                    Err(Error::new(
378                        EfwProtocolError::FlashBusy,
379                        "The flash memory is locked",
380                    ))
381                } else {
382                    self.memory[pos..(pos + BLOCK_SIZE)].fill(0);
383                    Ok(())
384                }
385            }
386        }
387
388        fn read_data(&self, args: &[u32], params: &mut Vec<u32>) -> Result<(), Error> {
389            if args.len() < 2 {
390                Err(Error::new(
391                    EfwProtocolError::BadCommand,
392                    "Argument is shorter than expected",
393                ))
394            } else {
395                let offset = args[0] as usize;
396                let count = args[1] as usize;
397                if count >= BLOCK_SIZE {
398                    let msg = "The count of data should be less than size of block";
399                    Err(Error::new(EfwProtocolError::BadCommand, &msg))
400                } else if offset + 4 * count > self.memory.len() {
401                    Err(Error::new(
402                        EfwProtocolError::BadCommand,
403                        "The offset plus count is out of range",
404                    ))
405                } else {
406                    if params.len() < 2 + count {
407                        Err(Error::new(
408                            EfwProtocolError::BadParameter,
409                            "Parameter is shorter than expected",
410                        ))
411                    } else {
412                        params[0] = offset as u32;
413                        params[1] = count as u32;
414
415                        let mut quadlet = [0; 4];
416                        params[2..].iter_mut().enumerate().for_each(|(i, d)| {
417                            let pos = offset as usize + i * 4;
418                            quadlet.copy_from_slice(&self.memory[pos..(pos + 4)]);
419                            *d = u32::from_be_bytes(quadlet);
420                        });
421                        Ok(())
422                    }
423                }
424            }
425        }
426
427        fn write_data(&mut self, args: &[u32], params: &mut Vec<u32>) -> Result<(), Error> {
428            if params.len() > 0 {
429                Err(Error::new(
430                    EfwProtocolError::BadParameter,
431                    "Useless parameter is given",
432                ))
433            } else if args.len() < 3 {
434                Err(Error::new(
435                    EfwProtocolError::BadCommand,
436                    "Argument is shorter than expected",
437                ))
438            } else {
439                let offset = args[0] as usize;
440                if offset < BLOCK_SIZE {
441                    Err(Error::new(
442                        EfwProtocolError::BadCommand,
443                        "The first block is immutable",
444                    ))
445                } else {
446                    let count = args[1] as usize;
447                    let data = &args[2..];
448
449                    if data.len() < count {
450                        Err(Error::new(
451                            EfwProtocolError::BadCommand,
452                            "Contradiction between count and data",
453                        ))
454                    } else if data.len() > BLOCK_QUADLET_COUNT {
455                        let msg = "The count of data should be less than size of block";
456                        Err(Error::new(EfwProtocolError::BadCommand, msg))
457                    } else if offset + 4 * data.len() > self.memory.len() {
458                        Err(Error::new(
459                            EfwProtocolError::BadCommand,
460                            "The offset plus length is out of range",
461                        ))
462                    } else if self.locked {
463                        Err(Error::new(
464                            EfwProtocolError::FlashBusy,
465                            "The flash memory is locked",
466                        ))
467                    } else {
468                        data.iter().enumerate().for_each(|(i, d)| {
469                            let pos = offset + i * 4;
470                            self.memory[pos..(pos + 4)].copy_from_slice(&d.to_be_bytes());
471                        });
472                        Ok(())
473                    }
474                }
475            }
476        }
477
478        fn get_status(&self, args: &[u32], params: &mut Vec<u32>) -> Result<(), Error> {
479            if args.len() > 0 {
480                Err(Error::new(
481                    EfwProtocolError::BadCommand,
482                    "Useless argument is given",
483                ))
484            } else if params.len() > 0 {
485                Err(Error::new(
486                    EfwProtocolError::BadParameter,
487                    "Useless parameter is given",
488                ))
489            } else if self.locked {
490                Err(Error::new(
491                    EfwProtocolError::FlashBusy,
492                    "The flash memory is locked",
493                ))
494            } else {
495                Ok(())
496            }
497        }
498
499        fn get_session_base(&self, args: &[u32], params: &mut Vec<u32>) -> Result<(), Error> {
500            if args.len() > 0 {
501                Err(Error::new(
502                    EfwProtocolError::BadCommand,
503                    "Useless argument is given",
504                ))
505            } else if params.len() < 1 {
506                Err(Error::new(
507                    EfwProtocolError::BadParameter,
508                    "Parameter is shorter than expected",
509                ))
510            } else {
511                params[0] = BLOCK_SIZE as u32;
512                Ok(())
513            }
514        }
515
516        fn lock_memory(&mut self, args: &[u32], params: &mut Vec<u32>) -> Result<(), Error> {
517            if params.len() > 0 {
518                Err(Error::new(
519                    EfwProtocolError::BadCommand,
520                    "Useless parameter is given",
521                ))
522            } else if args.len() < 1 {
523                Err(Error::new(
524                    EfwProtocolError::BadParameter,
525                    "Argument is shorter than expected",
526                ))
527            } else {
528                self.locked = args[0] > 0;
529                Ok(())
530            }
531        }
532    }
533
534    impl EfwProtocolExtManual for TestInstance {
535        fn transaction(
536            &self,
537            category: u32,
538            command: u32,
539            args: &[u32],
540            params: &mut Vec<u32>,
541            _: u32,
542        ) -> Result<(), glib::Error> {
543            assert_eq!(category, CATEGORY_FLASH);
544            match command {
545                CMD_ERASE => self.0.borrow_mut().erase_block(args, params),
546                CMD_READ => self.0.borrow_mut().read_data(args, params),
547                CMD_WRITE => self.0.borrow_mut().write_data(args, params),
548                CMD_STATUS => self.0.borrow_mut().get_status(args, params),
549                CMD_SESSION_BASE => self.0.borrow_mut().get_session_base(args, params),
550                CMD_LOCK => self.0.borrow_mut().lock_memory(args, params),
551                _ => unreachable!(),
552            }
553        }
554
555        fn emit_responded(&self, _: u32, _: u32, _: u32, _: u32, _: EfwProtocolError, _: &[u32]) {
556            // Omitted.
557        }
558
559        fn connect_responded<F>(&self, _f: F) -> SignalHandlerId
560        where
561            F: Fn(&Self, u32, u32, u32, u32, EfwProtocolError, &[u32]) + 'static,
562        {
563            // Dummy.
564            unsafe { SignalHandlerId::from_glib(0) }
565        }
566    }
567}