use log::info;
use rusb::{Direction, TransferType, UsbContext};
use crate::error::Error;
use crate::error::Result;
pub(crate) fn is_ippusb_interface(descriptor: &rusb::InterfaceDescriptor) -> bool {
descriptor.class_code() == 0x07
&& descriptor.sub_class_code() == 0x01
&& descriptor.protocol_code() == 0x04
}
pub fn device_supports_ippusb<T: UsbContext>(device: &rusb::Device<T>) -> Result<bool> {
match IppusbDeviceInfo::new(device) {
Ok(_) => Ok(true),
Err(Error::NotIppUsb) => Ok(false),
Err(e) => Err(e),
}
}
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) struct IppusbDescriptor {
pub interface_number: u8,
pub alternate_setting: u8,
pub in_endpoint: u8,
pub out_endpoint: u8,
}
pub(crate) struct IppusbDeviceInfo {
pub config: u8,
pub interfaces: Vec<IppusbDescriptor>,
}
impl IppusbDeviceInfo {
pub(crate) fn new<T: UsbContext>(device: &rusb::Device<T>) -> Result<Self> {
let desc = device
.device_descriptor()
.map_err(Error::ReadDeviceDescriptor)?;
for i in 0..desc.num_configurations() {
let config = device
.config_descriptor(i)
.map_err(Error::ReadConfigDescriptor)?;
let mut interfaces = Vec::new();
for interface in config.interfaces() {
'alternates: for alternate in interface.descriptors() {
if !is_ippusb_interface(&alternate) {
continue;
}
info!(
concat!(
"Device {}:{} - Found IPP-USB interface. ",
"config {}, interface {}, alternate {}"
),
device.bus_number(),
device.address(),
config.number(),
interface.number(),
alternate.setting_number()
);
let mut in_endpoint: Option<u8> = None;
let mut out_endpoint: Option<u8> = None;
for endpoint in alternate.endpoint_descriptors() {
match (endpoint.direction(), endpoint.transfer_type()) {
(Direction::In, TransferType::Bulk) => {
in_endpoint.get_or_insert(endpoint.address());
}
(Direction::Out, TransferType::Bulk) => {
out_endpoint.get_or_insert(endpoint.address());
}
_ => {}
};
if in_endpoint.is_some() && out_endpoint.is_some() {
break;
}
}
if let (Some(in_endpoint), Some(out_endpoint)) = (in_endpoint, out_endpoint) {
interfaces.push(IppusbDescriptor {
interface_number: interface.number(),
alternate_setting: alternate.setting_number(),
in_endpoint,
out_endpoint,
});
break 'alternates;
}
}
}
if interfaces.len() >= 2 {
return Ok(Self {
config: config.number(),
interfaces,
});
}
}
Err(Error::NotIppUsb)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compare_lowest_interface_first() {
let lhs = IppusbDescriptor {
interface_number: 1,
alternate_setting: 1,
in_endpoint: 1,
out_endpoint: 2,
};
let rhs = IppusbDescriptor {
interface_number: 0,
alternate_setting: 2,
in_endpoint: 3,
out_endpoint: 4,
};
assert!(rhs < lhs);
}
#[test]
fn compare_lowest_alternate_first() {
let lhs = IppusbDescriptor {
interface_number: 1,
alternate_setting: 2,
in_endpoint: 1,
out_endpoint: 2,
};
let rhs = IppusbDescriptor {
interface_number: 1,
alternate_setting: 1,
in_endpoint: 3,
out_endpoint: 4,
};
assert!(rhs < lhs);
}
}