use crate::protocol::opcode::{
PATCH_SEC_BOS, PATCH_SEC_CONFIG, PATCH_SEC_DEVICE, PATCH_SEC_REPORT, PATCH_SEC_STRING,
};
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub enum PatchSection {
#[default]
Device = PATCH_SEC_DEVICE,
Config = PATCH_SEC_CONFIG,
Report = PATCH_SEC_REPORT,
String = PATCH_SEC_STRING,
Bos = PATCH_SEC_BOS,
}
impl PatchSection {
pub fn as_u8(self) -> u8 {
self as u8
}
pub fn from_u8(v: u8) -> Option<PatchSection> {
Some(match v {
PATCH_SEC_DEVICE => PatchSection::Device,
PATCH_SEC_CONFIG => PatchSection::Config,
PATCH_SEC_REPORT => PatchSection::Report,
PATCH_SEC_STRING => PatchSection::String,
PATCH_SEC_BOS => PatchSection::Bos,
_ => return None,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Patch {
pub section: PatchSection,
pub cfg: u8,
pub index: u8,
pub offset: u16,
pub bytes: Vec<u8>,
}
impl Patch {
pub fn new(section: PatchSection, offset: u16, bytes: impl Into<Vec<u8>>) -> Patch {
Patch {
section,
cfg: 0,
index: 0,
offset,
bytes: bytes.into(),
}
}
pub fn in_config(cfg: u8, offset: u16, bytes: impl Into<Vec<u8>>) -> Patch {
Patch {
section: PatchSection::Config,
cfg,
index: 0,
offset,
bytes: bytes.into(),
}
}
pub fn in_interface(cfg: u8, interface: u8, offset: u16, bytes: impl Into<Vec<u8>>) -> Patch {
Patch {
section: PatchSection::Report,
cfg,
index: interface,
offset,
bytes: bytes.into(),
}
}
pub fn in_string(index: u8, bytes: impl Into<Vec<u8>>) -> Patch {
Patch {
section: PatchSection::String,
cfg: 0,
index,
offset: 0,
bytes: bytes.into(),
}
}
pub fn key(&self) -> PatchKey {
PatchKey {
section: self.section,
cfg: self.cfg,
index: self.index,
offset: self.offset,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PatchKey {
pub section: PatchSection,
pub cfg: u8,
pub index: u8,
pub offset: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PatchEntry {
pub section: PatchSection,
pub cfg: u8,
pub index: u8,
pub offset: u16,
pub len: u16,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PatchSet {
pub applied: bool,
pub pending: bool,
pub refused: bool,
pub table_full: bool,
pub entries: Vec<PatchEntry>,
}
impl PatchSet {
pub(crate) fn from_payload(p: &[u8]) -> Option<PatchSet> {
if p.len() < 3 {
return None;
}
let flags = p[1];
let n = p[2] as usize;
let mut entries = Vec::with_capacity(n);
for i in 0..n {
let o = 3 + 7 * i;
let row = p.get(o..o + 7)?;
let Some(section) = PatchSection::from_u8(row[0]) else {
continue;
};
entries.push(PatchEntry {
section,
cfg: row[1],
index: row[2],
offset: u16::from_le_bytes([row[3], row[4]]),
len: u16::from_le_bytes([row[5], row[6]]),
});
}
Some(PatchSet {
applied: flags & 0x01 != 0,
pending: flags & 0x02 != 0,
refused: flags & 0x04 != 0,
table_full: flags & 0x08 != 0,
entries,
})
}
}
pub(crate) fn patch_entry_from_payload(p: &[u8]) -> Option<Patch> {
let hdr = p.get(0..7)?;
let section = PatchSection::from_u8(hdr[2])?;
Some(Patch {
section,
cfg: hdr[3],
index: hdr[4],
offset: u16::from_le_bytes([hdr[5], hdr[6]]),
bytes: p.get(7..)?.to_vec(),
})
}