blob: 6cfbafbd6c759f1bab4741a7777626508958d794 [file] [log] [blame]
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::collections::VecDeque;
use std::io::{self, Read, Write};
#[cfg(target_os = "android")]
use std::os::fd::RawFd;
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
use std::vec::Vec;
use log::{debug, error, info};
use rusb::{Context, UsbContext};
use std::sync::{Condvar, Mutex};
use crate::device_info::{is_ippusb_interface, IppusbDescriptor, IppusbDeviceInfo};
use crate::error::Error;
use crate::error::Result;
const USB_TRANSFER_TIMEOUT: Duration = Duration::from_secs(60);
const USB_CLEANUP_TIMEOUT: Duration = Duration::from_secs(2);
fn interface_contains_ippusb(interface: &rusb::Interface) -> bool {
for descriptor in interface.descriptors() {
if is_ippusb_interface(&descriptor) {
return true;
}
}
false
}
fn set_device_config<T: UsbContext>(handle: &rusb::DeviceHandle<T>, new_config: u8) -> Result<()> {
let cur_config = handle
.device()
.active_config_descriptor()
.map_err(Error::ReadConfigDescriptor)?;
// While detaching any outstanding kernel drivers for the current config, keep
// track of non-printer drivers so we can restore them after setting the config.
let mut restore_interfaces = Vec::new();
for interface in cur_config.interfaces() {
if !interface_contains_ippusb(&interface) {
match handle.kernel_driver_active(interface.number()) {
Ok(false) => continue, // No active driver.
Err(e) => return Err(Error::DetachDrivers(interface.number(), e)),
_ => {}
}
info!(
"Temporarily detaching kernel driver for non-printer interface {}",
interface.number()
);
restore_interfaces.push(interface.number());
}
match handle.detach_kernel_driver(interface.number()) {
Err(e) if e != rusb::Error::NotFound => {
return Err(Error::DetachDrivers(interface.number(), e))
}
_ => {}
}
}
info!(
"Switching from configuration {} to {}",
cur_config.number(),
new_config
);
handle
.set_active_configuration(new_config)
.map_err(Error::SetActiveConfig)?;
// Try to put back the previously detached drivers. We don't return an error if one
// of these fails because it won't prevent us from claiming the IPP-USB interfaces later.
for inum in restore_interfaces {
handle
.attach_kernel_driver(inum)
.unwrap_or_else(|e| error!("Failed to reattach driver for interface {}: {}", inum, e));
}
Ok(())
}
struct ClaimedInterface {
handle: Arc<rusb::DeviceHandle<Context>>,
descriptor: IppusbDescriptor,
}
impl ClaimedInterface {
/// Send a USB claim for our interface and switch it to the correct alternate setting.
fn claim(&mut self) -> Result<()> {
self.handle
.claim_interface(self.descriptor.interface_number)
.map_err(|e| Error::ClaimInterface(self.descriptor.interface_number, e))?;
self.handle
.set_alternate_setting(
self.descriptor.interface_number,
self.descriptor.alternate_setting,
)
.map_err(|e| Error::SetAlternateSetting(self.descriptor.interface_number, e))
}
/// Send a USB release for our interface.
fn release(&mut self) -> Result<()> {
self.handle
.release_interface(self.descriptor.interface_number)
.map_err(|e| Error::ReleaseInterface(self.descriptor.interface_number, e))
}
}
/// InterfaceManagerState contains the internal state of InterfaceManager. It is intended to
/// be shared across InterfaceManager instances and protected by a mutex.
struct InterfaceManagerState {
interfaces: VecDeque<ClaimedInterface>,
handle: Arc<rusb::DeviceHandle<Context>>,
usb_config: u8,
active: usize,
pending_cleanup: bool,
next_cleanup: Instant,
}
impl InterfaceManagerState {
fn claim_all(&mut self) -> Result<()> {
// Detach any outstanding kernel drivers for the current config before attempting
// to switch to our desired config.
let config = self
.handle
.device()
.active_config_descriptor()
.map_err(Error::ReadConfigDescriptor)?;
if config.number() != self.usb_config {
set_device_config(self.handle.as_ref(), self.usb_config)?;
}
for interface in &mut self.interfaces {
if let Err(e) = interface.claim() {
// We don't bother to free any successfully claimed interfaces because
// it's not an error to try to claim them again when the next connection
// arrives.
error!(
"Failed to reclaim interface {}: {}",
interface.descriptor.interface_number, e
);
return Err(e);
}
}
Ok(())
}
fn release_all(&mut self) -> Result<()> {
for interface in &mut self.interfaces {
interface.release()?;
}
Ok(())
}
}
/// InterfaceManager is responsible for managing a pool of claimed USB interfaces.
/// At construction, it is provided with a set of interfaces, and then clients
/// can use its member functions in order to request and free interfaces.
///
/// If no interfaces are currently available, requesting an interface will block
/// until an interface is freed by another thread.
///
/// InterfaceManager maintains the invariant that interfaces are claimed when
/// handed out. It expects newly-inserted interfaces to be claimed by libusb and
/// it ensures that they are still claimed when retrieved. Internally it releases
/// and claims free interfaces to allow sharing with other programs that might need
/// to access the USB interfaces.
#[derive(Clone)]
struct InterfaceManager {
interface_available: Arc<Condvar>,
state: Arc<Mutex<InterfaceManagerState>>,
}
impl InterfaceManager {
fn new(
handle: Arc<rusb::DeviceHandle<Context>>,
usb_config: u8,
interfaces: Vec<ClaimedInterface>,
) -> Self {
let mut deque: VecDeque<ClaimedInterface> = interfaces.into();
for interface in &mut deque {
interface.release().unwrap_or_else(|e| {
error!(
"Failed to release interface {}: {}",
interface.descriptor.interface_number, e
);
});
}
Self {
interface_available: Arc::new(Condvar::new()),
state: Arc::new(Mutex::new(InterfaceManagerState {
interfaces: deque,
handle,
usb_config,
active: 0,
pending_cleanup: false,
next_cleanup: Instant::now(),
})),
}
}
/// Start a separate thread to release interfaces. Interfaces are released once
/// USB_CLEANUP_TIMEOUT elapses with no activity after all interfaces are internally
/// returned.
fn start_cleanup_thread(&mut self) -> Result<std::thread::JoinHandle<()>> {
let manager = self.clone();
let handle = thread::Builder::new()
.name("interface cleanup".into())
.spawn(move || {
debug!("Cleanup thread starting");
let mut state = manager.state.lock().unwrap();
// We wait in two phases:
// 1. As long as no cleanup is pending or there is an active interface, we need to
// wait indefinitely. Each interface sets pending_cleanup when it is returned,
// so this will eventually drop to 0 active interfaces with a cleanup pending.
// 2. Once cleanup is needed and there are no active interfaces, we wait for a
// timeout. If another connection comes during the timeout, we go back to the
// previous phase.
'outer: loop {
// Phase 1: Wait for cleanup to be needed and all active interfaces to be
// returned.
state = manager
.interface_available
.wait_while(state, |t| t.active > 0 || !t.pending_cleanup)
.unwrap();
// Phase 2: Wait for the cleanup time to arrive.
// 1. If an interface is claimed and returned during the timeout, we will
// detect this because the timeout will be extended. We can simply wait
// more if this happens.
// 2. If an interface is claimed and is still active after our timeout, there's
// no need to keep waking up. Go back to phase 1 and wait for the active
// interfaces to be returned.
// 3. Once everything remains the same for the whole timeout period, we can
// exit the while loop and release all the interfaces.
while state.next_cleanup > Instant::now() {
let wait = state.next_cleanup - Instant::now();
let result = manager
.interface_available
.wait_timeout_while(state, wait, |t| t.active == 0)
.unwrap();
state = result.0; // Throw away the timed out part of the result.
if state.active > 0 {
continue 'outer;
}
}
// Now we know that there must be no active clients and the cleanup time must
// have arrived:
// 1. If there were an active client, the check inside the while loop above
// would have gone back to the outer loop.
// 2. If the cleanup time hadn't arrived, the inner while loop wouldn't have
// exited.
// This means we can safely release all the interfaces without affecting any
// active connections.
assert_eq!(state.active, 0, "Active interfaces not expected");
assert!(
Instant::now() >= state.next_cleanup,
"Cleanup time not arrived"
);
debug!("Releasing all USB interfaces");
match state.release_all() {
Ok(()) => {}
// If the device was unplugged, don't bother to print an error. We're
// about to exit anyway.
Err(Error::ReleaseInterface(_, rusb::Error::NoDevice)) => {}
// If this failed for some other reason, we're in some bad state. There
// are no active interfaces, so restarting won't interrupt an active
// connection. We should just quit and let upstart reset us back to a
// known good state.
Err(e) => panic!("Failed to release interfaces: {}", e),
};
state.pending_cleanup = false;
}
})
.map_err(Error::CleanupThread)?;
Ok(handle)
}
/// Get an interface from the pool of tracked interfaces.
/// Will block until an interface is available.
/// If interfaces are currently not claimed, will first set the active device
/// configuration and re-claim USB interfaces.
fn request_interface(&mut self) -> Result<ClaimedInterface> {
let mut state = self.state.lock().unwrap();
if state.active == 0 && !state.pending_cleanup {
debug!("Claiming all interfaces");
state.claim_all()?;
state.pending_cleanup = true;
}
state.active += 1;
loop {
if let Some(interface) = state.interfaces.pop_front() {
debug!(
"* Using interface {}",
interface.descriptor.interface_number
);
return Ok(interface);
}
state = self.interface_available.wait(state).unwrap();
}
}
/// Return an interface to the pool of interfaces.
fn free_interface(&mut self, interface: ClaimedInterface) {
debug!(
"* Returning interface {}",
interface.descriptor.interface_number
);
let mut state = self.state.lock().unwrap();
state.interfaces.push_back(interface);
state.next_cleanup = Instant::now() + USB_CLEANUP_TIMEOUT;
state.pending_cleanup = true;
state.active -= 1;
// Use notify_all instead of notify_one because the cleanup thread may also be
// waiting on this condition variable.
self.interface_available.notify_all();
}
}
/// An opened IPP-USB device.
///
/// `Device` itself manages a pool of IPP-USB interfaces, but does not perform I/O. Users
/// can temporarily request a `Connection` from the `Device` using `get_connection()`. The
/// `Connection` can be used to perform I/O to the device.
#[derive(Clone)]
pub struct Device {
verbose_log: bool,
handle: Arc<rusb::DeviceHandle<Context>>,
manager: InterfaceManager,
}
impl Device {
/// Create a new `Device` to wrap an `rusb::DeviceHandle`.
///
/// The device will be reset into the correct configuration and all IPP-USB interfaces will be
/// claimed. A background thread will be started to manage the active interface pool.
///
/// Returns an error if the device does not support IPP-USB or an error occurs during the
/// initialization described above.
pub fn new(verbose_log: bool, handle: rusb::DeviceHandle<Context>) -> Result<Device> {
let handle = Arc::new(handle);
handle
.set_auto_detach_kernel_driver(true)
.map_err(|e| Error::DetachDrivers(u8::MAX, e))?; // Use MAX to mean "no interface".
let info = IppusbDeviceInfo::new(&handle.device())?;
set_device_config(handle.as_ref(), info.config)?;
// Open the IPP-USB interfaces.
let mut connections = Vec::new();
for descriptor in info.interfaces {
handle
.claim_interface(descriptor.interface_number)
.map_err(|e| Error::ClaimInterface(descriptor.interface_number, e))?;
handle
.set_alternate_setting(descriptor.interface_number, descriptor.alternate_setting)
.map_err(|e| Error::SetAlternateSetting(descriptor.interface_number, e))?;
connections.push(ClaimedInterface {
handle: handle.clone(),
descriptor,
});
}
let mut manager = InterfaceManager::new(handle.clone(), info.config, connections);
manager.start_cleanup_thread()?;
Ok(Device {
verbose_log,
handle,
manager,
})
}
/// Return the contained device.
pub fn device(&self) -> rusb::Device<Context> {
self.handle.device()
}
/// Return a `Connection` representing a claimed IPP-USB interface.
///
/// The returned interface can be used for I/O with the USB device. Returns an error if no
/// IPP-USB interfaces are currently available or if claiming the interface fails.
pub fn get_connection(&mut self) -> Result<Connection> {
let interface = self.manager.request_interface()?;
Ok(Connection::new(
self.verbose_log,
self.manager.clone(),
interface,
))
}
}
/// A struct representing a claimed IPP-USB interface.
///
/// The owner of this struct can communicate with the IPP-USB device via the Read and Write traits.
pub struct Connection {
verbose_log: bool,
manager: InterfaceManager,
// `interface` is never None until the Connection is dropped, at which point the
// ClaimedInterface is returned to the pool of connections in InterfaceManager.
interface: Option<ClaimedInterface>,
}
impl Connection {
fn new(verbose_log: bool, manager: InterfaceManager, interface: ClaimedInterface) -> Self {
Self {
verbose_log,
manager,
interface: Some(interface),
}
}
}
impl Drop for Connection {
fn drop(&mut self) {
// Unwrap because interface only becomes None at drop.
let interface = self.interface.take().unwrap();
self.manager.free_interface(interface);
}
}
fn to_io_error(err: rusb::Error) -> io::Error {
let kind = match err {
rusb::Error::InvalidParam => io::ErrorKind::InvalidInput,
rusb::Error::NotFound => io::ErrorKind::NotFound,
rusb::Error::Timeout => io::ErrorKind::TimedOut,
rusb::Error::Pipe => io::ErrorKind::BrokenPipe,
rusb::Error::Interrupted => io::ErrorKind::Interrupted,
_ => io::ErrorKind::Other,
};
io::Error::new(kind, err)
}
impl Write for &Connection {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
// Unwrap because interface only becomes None at drop.
let interface = self.interface.as_ref().unwrap();
let endpoint = interface.descriptor.out_endpoint;
let written = interface
.handle
.write_bulk(endpoint, buf, USB_TRANSFER_TIMEOUT)
.map_err(to_io_error)?;
if self.verbose_log {
debug!("USB write: {} bytes", written);
}
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Read for Connection {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
// Unwrap because interface only becomes None at drop.
let interface = self.interface.as_ref().unwrap();
let endpoint = interface.descriptor.in_endpoint;
let start = Instant::now();
let mut result = interface
.handle
.read_bulk(endpoint, buf, USB_TRANSFER_TIMEOUT)
.map_err(to_io_error);
let mut zero_reads = 0;
// USB reads cannot hit EOF. We will retry after a short delay so that higher-level
// readers can pretend this doesn't exist.
while let Ok(0) = result {
zero_reads += 1;
if start.elapsed() > USB_TRANSFER_TIMEOUT {
result = Err(io::Error::new(
io::ErrorKind::TimedOut,
"Timed out waiting for non-zero USB read",
));
break;
}
thread::sleep(Duration::from_millis(10));
result = interface
.handle
.read_bulk(endpoint, buf, USB_TRANSFER_TIMEOUT)
.map_err(to_io_error);
}
if self.verbose_log {
if let Ok(num) = result {
debug!("USB read: {} bytes", num);
}
}
if zero_reads > 0 {
debug!(
"Spent {}ms waiting for {} 0-byte USB reads",
start.elapsed().as_millis(),
zero_reads
);
}
result
}
}