use super::{PROBE_TIMEOUT, READY_TIMEOUT};
use crate::{
error::Error,
protocol::{
caps::{
Capabilities, Page,
address::{Address, CoordinateBase},
ccd::CcdMeasurement,
frames::Frames,
identity::Identity,
other::Features,
set_window::SetWindowFunction,
},
cdbs::Inquiry,
sense::{Fault, Outcome, interpret},
},
transport::{Data, Transport},
};
use std::time::Duration;
use tracing::*;
pub fn inquiry(t: &mut dyn Transport, cmd: Inquiry) -> Result<Vec<u8>, Error> {
inquiry_within(t, cmd, PROBE_TIMEOUT)
}
pub fn inquiry_within(
t: &mut dyn Transport,
cmd: Inquiry,
timeout: Duration,
) -> Result<Vec<u8>, Error> {
let mut buf = vec![0u8; cmd.allocation_length()];
let completion = t.execute(&cmd.cdb(), Data::In(&mut buf), timeout)?;
match interpret(&completion) {
Outcome::Complete | Outcome::CompleteWith(_) => {}
other => return Err(Error::from_outcome(other, &completion)),
}
buf.truncate(completion.transferred);
Ok(buf)
}
pub fn vpd(t: &mut dyn Transport, code: u8) -> Result<Page, Error> {
let bytes = inquiry(t, Inquiry::vpd(code))?;
if enabled!(Level::TRACE) {
let hex: Vec<String> = bytes.iter().map(|b| format!("{b:02X}")).collect();
trace!(page = format!("{code:02X}h"), bytes = hex.join(" "), "vpd");
}
Ok(Page::new(code, bytes)?)
}
pub const UNLISTED: &[u8] = &[CcdMeasurement::PAGE_CODE];
pub fn page_codes(t: &mut dyn Transport) -> Result<Vec<u8>, Error> {
let list = inquiry(t, Inquiry::vpd(0x00))?;
let length = usize::from(*list.get(3).unwrap_or(&0));
let mut codes: Vec<u8> = list.get(4..4 + length).unwrap_or_default().to_vec();
codes.retain(|&code| code != 0x00);
let missing: Vec<u8> = UNLISTED
.iter()
.copied()
.filter(|code| !codes.contains(code))
.collect();
codes.extend(missing);
Ok(codes)
}
pub fn capabilities(t: &mut dyn Transport) -> Result<Capabilities, Error> {
let identity = Identity::parse(&inquiry_within(t, Inquiry::standard(), READY_TIMEOUT)?)?;
if !identity.is_scanner() {
return Err(Error::NotFound);
}
let address = Address::try_from(&vpd(t, Address::PAGE_CODE)?)?;
let frames = match address
.coordinate_base
.contains(CoordinateBase::FRAME_RECTS)
{
true => Some(Frames::try_from(&vpd(t, Frames::PAGE_CODE)?)?),
false => None,
};
let features = Features::try_from(&vpd(t, Features::PAGE_CODE)?)?;
debug!(
page_length = features.page_length,
cooperation = ?features.cooperation,
execute = ?features.execute,
"features"
);
Ok(Capabilities {
identity,
address,
features,
set_window: SetWindowFunction::try_from(&vpd(t, SetWindowFunction::PAGE_CODE)?)?,
ccd: match vpd(t, CcdMeasurement::PAGE_CODE) {
Ok(page) => Some(CcdMeasurement::try_from(&page)?),
Err(Error::Device(fault)) if matches!(*fault, Fault::Rejected(..)) => {
debug!("this unit has no CCD measurement page");
None
}
Err(e) => return Err(e),
},
frames,
})
}