use std::time::Duration;
use rusb::{Context, Device, DeviceHandle, UsbContext};
use tracing::debug;
use crate::{error::UsbError, printer::PrinterModel};
use super::{PrinterConnection, printer_connection::sealed::ConnectionImpl};
const BROTHER_USB_VENDOR_ID: u16 = 0x04f9;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UsbConnectionInfo {
pub(crate) vendor_id: u16,
pub(crate) product_id: u16,
pub(crate) interface: u8,
pub(crate) endpoint_out: u8,
pub(crate) endpoint_in: u8,
pub(crate) timeout: Duration,
}
impl UsbConnectionInfo {
#[must_use]
pub const fn from_model(model: PrinterModel) -> Self {
Self {
vendor_id: BROTHER_USB_VENDOR_ID,
product_id: model.product_id(),
interface: 0,
endpoint_out: 0x02,
endpoint_in: 0x81,
timeout: Duration::from_millis(5000),
}
}
pub fn discover() -> Result<Option<Self>, UsbError> {
let context = Context::new()?;
let devices = context.devices()?;
for device in devices.iter() {
let descriptor = device.device_descriptor()?;
if descriptor.vendor_id() == BROTHER_USB_VENDOR_ID
&& let Some(model) = PrinterModel::from_product_id(descriptor.product_id())
{
return Ok(Some(Self::from_model(model)));
}
}
Ok(None)
}
}
pub struct UsbConnection {
handle: DeviceHandle<Context>,
interface: u8,
timeout: Duration,
endpoint_out: u8,
endpoint_in: u8,
}
impl UsbConnection {
pub fn open(info: UsbConnectionInfo) -> Result<Self, UsbError> {
debug!("Opening USB Connection to the printer...");
let context = Context::new()?;
let device = Self::find_device(&context, info.vendor_id, info.product_id)?;
let handle = device.open()?;
handle.set_auto_detach_kernel_driver(true)?;
if handle.kernel_driver_active(0)? {
handle.detach_kernel_driver(0)?;
}
handle.set_active_configuration(1)?;
handle.claim_interface(info.interface)?;
if let Err(e) = handle.set_alternate_setting(info.interface, 0) {
let _ = handle.release_interface(info.interface);
return Err(e.into());
}
debug!("Successfully established USB Connection!");
Ok(Self {
handle,
interface: info.interface,
timeout: info.timeout,
endpoint_out: info.endpoint_out,
endpoint_in: info.endpoint_in,
})
}
fn find_device(
context: &Context,
vendor_id: u16,
product_id: u16,
) -> Result<Device<Context>, UsbError> {
let devices = context.devices()?;
for device in devices.iter() {
let descriptor = device.device_descriptor()?;
if descriptor.vendor_id() == vendor_id && descriptor.product_id() == product_id {
return Ok(device);
}
}
Err(UsbError::DeviceNotFound {
vendor_id,
product_id,
})
}
}
impl PrinterConnection for UsbConnection {}
impl ConnectionImpl for UsbConnection {
type Error = UsbError;
fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
let bytes_written = self
.handle
.write_bulk(self.endpoint_out, data, self.timeout)?;
if bytes_written != data.len() {
return Err(UsbError::IncompleteWrite);
}
Ok(())
}
fn read(&mut self, buffer: &mut [u8]) -> Result<usize, Self::Error> {
let bytes_read = self
.handle
.read_bulk(self.endpoint_in, buffer, self.timeout)?;
Ok(bytes_read)
}
}
impl Drop for UsbConnection {
fn drop(&mut self) {
let _ = self.handle.release_interface(self.interface);
}
}