Skip to main content

ops/
params.rs

1//! Pushing parameter packs into the card's RAM, generated from a panel spec.
2//!
3//! The vendor tool re-sends the whole raster state on every push, not only the
4//! chip registers: pixel sequence, scan table and void tables live in RAM, and
5//! sending only the three type-0x05 packs leaves them as the card booted.
6
7use crate::util::open;
8use crate::{protocol, rcvbp, Ctx, Loader};
9use anyhow::Result;
10use panelspec::PanelSpec;
11use rawlink::Link;
12use rcvbp::image;
13use rcvbp::spec::Generated;
14use std::time::Duration;
15
16/// Image offsets of the second halves of the void-line and anti-void tables,
17/// which `rcvbp::image` does not name.
18const VOID_LINE_HIGH_OFFSET: usize = 0x6800;
19const ANTI_VOID_HIGH_OFFSET: usize = 0x7000;
20
21/// One real-time pack: wire type, sub-index, header length, body.
22struct Pack<'a> {
23    kind: u8,
24    sub: u8,
25    header: usize,
26    body: &'a [u8],
27}
28
29impl Pack<'_> {
30    /// Frame the pack the way `SendRealTimePacks` does: the pack's first two
31    /// bytes are the EtherType, the body sits at the type's header offset.
32    fn send(&self, dev: &mut Link, gap: Duration) -> Result<()> {
33        let mut p = vec![0u8; self.header - 2 + self.body.len()];
34        p[1] = self.sub;
35        p[self.header - 2..].copy_from_slice(self.body);
36        dev.send(&protocol::frame([self.kind, 0x00], &p))?;
37        std::thread::sleep(gap);
38        Ok(())
39    }
40}
41
42/// Push the real-time parameter packs for a panel spec file, in the vendor's
43/// order. RAM only: no flash, no reboot.
44pub fn send_params(
45    ctx: &Ctx,
46    spec_path: &str,
47    chip_only: bool,
48    gap_ms: u64,
49    load: Loader,
50) -> Result<()> {
51    let spec = PanelSpec::load(spec_path)?;
52    let g = rcvbp::spec::generate(&spec, &spec.chip_library(load)?)?;
53    send_generated(ctx, &spec, &g, chip_only, gap_ms)
54}
55
56/// [`send_params`] for a spec already generated.
57#[rustfmt::skip] // one pack per line reads as the vendor's send table
58pub fn send_generated(ctx: &Ctx, spec: &PanelSpec, g: &Generated, chip_only: bool, gap_ms: u64) -> Result<()> {
59    let gap = Duration::from_millis(gap_ms);
60    let map = &ctx.model()?.memory.boot_image;
61    let mut dev = open(ctx)?;
62
63    // Addressed-register chips get their table as the chip pack. A
64    // non-addressed chip carries its configuration inside the basic pack's
65    // chip-custom block and has no record 0x84 to send.
66    if let Some(r) = g.rcvbp.find_by_id(0x84) {
67        Pack { kind: 0x05, sub: protocol::params::SUB_CHIP, header: 4, body: &r.payload }
68            .send(&mut dev, gap)?;
69    }
70    if chip_only {
71        return Ok(());
72    }
73
74    // The rest of the raster state comes from the same regions the boot image
75    // carries, so the card gets in RAM exactly what it would boot with.
76    let img = image::Block7Builder::from_generated(map, spec, g)?.finish().image;
77
78    let mut packs: Vec<Pack> = vec![
79        Pack { kind: 0x05, sub: protocol::params::SUB_DATA_SWAP, header: 4,
80               body: &img[map.data_swap..map.data_swap + 0x100] },
81        Pack { kind: 0x05, sub: protocol::params::SUB_BASIC, header: 4, body: &g.basic_pack },
82        Pack { kind: 0x10, sub: 0, header: 4, body: &img[0x0100..0x0500] }, // void table
83        Pack { kind: 0x17, sub: 0, header: 5, body: &img[0x0600..0x0900] }, // module positions
84    ];
85    // Pixel sequence: the mapping table, sliced into 16 packs of 0x300.
86    for k in 0..16 {
87        let at = map.mapping + k * 0x300;
88        packs.push(Pack { kind: 0x03, sub: k as u8, header: 4, body: &img[at..at + 0x300] });
89    }
90    // Void-line and anti-void tables each split across two image regions
91    // (docs/compiled-image-format.md); the packs follow that split.
92    for k in 0..4usize {
93        let at = if k < 2 { map.void_line + k * 0x400 } else { VOID_LINE_HIGH_OFFSET + (k - 2) * 0x400 };
94        packs.push(Pack { kind: 0x1F, sub: k as u8, header: 8, body: &img[at..at + 0x400] });
95    }
96    for k in 0..8usize {
97        let at = if k < 4 { map.anti_void + k * 0x400 } else { ANTI_VOID_HIGH_OFFSET + (k - 4) * 0x400 };
98        packs.push(Pack { kind: 0x32, sub: k as u8, header: 8, body: &img[at..at + 0x400] });
99    }
100    packs.push(Pack { kind: 0x18, sub: 0, header: 4,
101                      body: &img[map.scan_table..map.scan_table + 0x400] });
102
103    for pk in &packs {
104        pk.send(&mut dev, gap)?;
105    }
106    Ok(())
107}