use crate::error::{ProtocolError, Result};
use crate::log_protocol;
use crate::logging::LogLevel;
const APCI_DEVICE_DESCRIPTOR_READ: u16 = 0x0300;
const APCI_DEVICE_DESCRIPTOR_RESPONSE: u16 = 0x0340;
const APCI_SERVICE_MASK: u16 = 0x03C0;
const DESCRIPTOR_MASK: u8 = 0x3F;
const APCI_HIGH_BYTE: u8 = 0x03;
const RESPONSE_BYTE_BITS: u8 = 0x40;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DeviceDescriptorRead {
pub descriptor: u8,
}
impl DeviceDescriptorRead {
#[must_use]
pub fn encode(&self) -> Vec<u8> {
let byte0 = ((APCI_DEVICE_DESCRIPTOR_READ >> 8) & 0x03) as u8;
let byte1 = (APCI_DEVICE_DESCRIPTOR_READ as u8) | (self.descriptor & DESCRIPTOR_MASK);
let result = vec![byte0, byte1];
log_protocol!(
LogLevel::Trace,
"Management encode: {:?} \u{2192} {:02X?}",
self,
result
);
result
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeviceDescriptorResponse {
pub descriptor: u8,
pub data: Vec<u8>,
}
impl DeviceDescriptorResponse {
#[must_use]
pub fn encode(&self) -> Vec<u8> {
let mut result = Vec::with_capacity(self.data.len() + 2);
result.push(APCI_HIGH_BYTE);
result.push(RESPONSE_BYTE_BITS | (self.descriptor & DESCRIPTOR_MASK));
result.extend_from_slice(&self.data);
log_protocol!(
LogLevel::Trace,
"Management encode: {:?} \u{2192} {} bytes",
self,
result.len()
);
result
}
pub fn decode(apci_bytes: &[u8]) -> Result<Self> {
if apci_bytes.len() < 2 {
return Err(ProtocolError::InvalidFrame {
details: format!(
"DeviceDescriptorResponse needs at least 2 APCI bytes, got {}",
apci_bytes.len()
),
}
.into());
}
let byte0 = apci_bytes[0];
let byte1 = apci_bytes[1];
let apci = (u16::from(byte0 & 0x03) << 8) | u16::from(byte1);
log_protocol!(
LogLevel::Trace,
"Management decode: apci=0x{:04X} len={}",
apci,
apci_bytes.len()
);
if (apci & APCI_SERVICE_MASK) != APCI_DEVICE_DESCRIPTOR_RESPONSE {
log_protocol!(
LogLevel::Warn,
"Management: not a DeviceDescriptorResponse (apci=0x{:04X})",
apci
);
return Err(ProtocolError::InvalidFrame {
details: format!(
"Expected DeviceDescriptorResponse APCI 0x{APCI_DEVICE_DESCRIPTOR_RESPONSE:04X}, \
got 0x{apci:04X}"
),
}
.into());
}
Ok(Self {
descriptor: byte1 & DESCRIPTOR_MASK,
data: apci_bytes[2..].to_vec(),
})
}
}
#[cfg(test)]
mod tests {
use super::{DeviceDescriptorRead, DeviceDescriptorResponse};
#[test]
fn device_descriptor_read_encodes_to_apci_0x0300() {
assert_eq!(
DeviceDescriptorRead { descriptor: 0 }.encode(),
vec![0x03, 0x00]
);
assert_eq!(
DeviceDescriptorRead { descriptor: 0x3F }.encode(),
vec![0x03, 0x3F]
);
}
#[test]
fn device_descriptor_read_masks_descriptor_to_six_bits() {
assert_eq!(
DeviceDescriptorRead { descriptor: 0xFF }.encode(),
vec![0x03, 0x3F]
);
}
#[test]
fn device_descriptor_response_round_trip() {
let response = DeviceDescriptorResponse {
descriptor: 0,
data: vec![0x07, 0xB0],
};
let encoded = response.encode();
assert_eq!(encoded, vec![0x03, 0x40, 0x07, 0xB0]);
assert_eq!(
DeviceDescriptorResponse::decode(&encoded).unwrap(),
response
);
}
#[test]
fn device_descriptor_response_decodes_known_bytes() {
let decoded = DeviceDescriptorResponse::decode(&[0x03, 0x40, 0x07, 0xB0]).unwrap();
assert_eq!(decoded.descriptor, 0);
assert_eq!(decoded.data, vec![0x07, 0xB0]);
}
#[test]
fn device_descriptor_response_preserves_descriptor_bits() {
let decoded = DeviceDescriptorResponse::decode(&[0x03, 0x42]).unwrap();
assert_eq!(decoded.descriptor, 0x02);
assert!(decoded.data.is_empty());
}
#[test]
fn device_descriptor_response_rejects_non_0x0340_pattern() {
assert!(DeviceDescriptorResponse::decode(&[0x03, 0x00]).is_err());
assert!(DeviceDescriptorResponse::decode(&[0x00, 0x40]).is_err());
}
#[test]
fn device_descriptor_response_rejects_short_input() {
assert!(DeviceDescriptorResponse::decode(&[]).is_err());
assert!(DeviceDescriptorResponse::decode(&[0x03]).is_err());
}
}