Define a new Error type instead of re-exporting std::io::Error
The default mapping from OS error codes to io::Error's messages is often wrong or misleading. We can do a lot better, but it takes a lot of error-handling code.
This commit is contained in:
parent
592f57a3e0
commit
6895347f4b
24 changed files with 737 additions and 353 deletions
|
|
@ -2,17 +2,11 @@
|
||||||
//!
|
//!
|
||||||
//! Descriptors are blocks of data that describe the functionality of a USB device.
|
//! Descriptors are blocks of data that describe the functionality of a USB device.
|
||||||
|
|
||||||
use std::{
|
use std::{collections::BTreeMap, fmt::Debug, iter, num::NonZeroU8, ops::Deref};
|
||||||
collections::BTreeMap,
|
|
||||||
fmt::{Debug, Display},
|
|
||||||
iter,
|
|
||||||
num::NonZeroU8,
|
|
||||||
ops::Deref,
|
|
||||||
};
|
|
||||||
|
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
||||||
use crate::{transfer::Direction, Error};
|
use crate::transfer::Direction;
|
||||||
|
|
||||||
pub(crate) const DESCRIPTOR_TYPE_DEVICE: u8 = 0x01;
|
pub(crate) const DESCRIPTOR_TYPE_DEVICE: u8 = 0x01;
|
||||||
pub(crate) const DESCRIPTOR_LEN_DEVICE: u8 = 18;
|
pub(crate) const DESCRIPTOR_LEN_DEVICE: u8 = 18;
|
||||||
|
|
@ -710,34 +704,6 @@ pub enum TransferType {
|
||||||
Interrupt = 3,
|
Interrupt = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error from [`crate::Device::active_configuration`]
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct ActiveConfigurationError {
|
|
||||||
pub(crate) configuration_value: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ActiveConfigurationError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
if self.configuration_value == 0 {
|
|
||||||
write!(f, "device is not configured")
|
|
||||||
} else {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"no descriptor found for active configuration {}",
|
|
||||||
self.configuration_value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for ActiveConfigurationError {}
|
|
||||||
|
|
||||||
impl From<ActiveConfigurationError> for Error {
|
|
||||||
fn from(value: ActiveConfigurationError) -> Self {
|
|
||||||
Error::other(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Split a chain of concatenated configuration descriptors by `wTotalLength`
|
/// Split a chain of concatenated configuration descriptors by `wTotalLength`
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) fn parse_concatenated_config_descriptors(
|
pub(crate) fn parse_concatenated_config_descriptors(
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
descriptors::{
|
descriptors::{
|
||||||
decode_string_descriptor, validate_string_descriptor, ActiveConfigurationError,
|
decode_string_descriptor, validate_string_descriptor, ConfigurationDescriptor,
|
||||||
ConfigurationDescriptor, DeviceDescriptor, InterfaceDescriptor, DESCRIPTOR_TYPE_STRING,
|
DeviceDescriptor, InterfaceDescriptor, DESCRIPTOR_TYPE_STRING,
|
||||||
},
|
},
|
||||||
platform,
|
platform,
|
||||||
transfer::{
|
transfer::{
|
||||||
Buffer, BulkOrInterrupt, Completion, ControlIn, ControlOut, Direction, EndpointDirection,
|
Buffer, BulkOrInterrupt, Completion, ControlIn, ControlOut, Direction, EndpointDirection,
|
||||||
EndpointType, TransferError,
|
EndpointType, TransferError,
|
||||||
},
|
},
|
||||||
DeviceInfo, Error, MaybeFuture, Speed,
|
ActiveConfigurationError, DeviceInfo, Error, ErrorKind, GetDescriptorError, MaybeFuture, Speed,
|
||||||
};
|
};
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use std::{
|
use std::{
|
||||||
future::{poll_fn, Future},
|
future::{poll_fn, Future},
|
||||||
io::ErrorKind,
|
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
num::NonZeroU8,
|
num::NonZeroU8,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
|
@ -51,9 +50,7 @@ impl Device {
|
||||||
Device { backend }
|
Device { backend }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn open(
|
pub(crate) fn open(d: &DeviceInfo) -> impl MaybeFuture<Output = Result<Device, Error>> {
|
||||||
d: &DeviceInfo,
|
|
||||||
) -> impl MaybeFuture<Output = Result<Device, std::io::Error>> {
|
|
||||||
platform::Device::from_device_info(d).map(|d| d.map(Device::wrap))
|
platform::Device::from_device_info(d).map(|d| d.map(Device::wrap))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,13 +178,14 @@ impl Device {
|
||||||
desc_index: u8,
|
desc_index: u8,
|
||||||
language_id: u16,
|
language_id: u16,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
) -> impl MaybeFuture<Output = Result<Vec<u8>, Error>> {
|
) -> impl MaybeFuture<Output = Result<Vec<u8>, GetDescriptorError>> {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
let _ = timeout;
|
let _ = timeout;
|
||||||
self.backend
|
self.backend
|
||||||
.clone()
|
.clone()
|
||||||
.get_descriptor(desc_type, desc_index, language_id)
|
.get_descriptor(desc_type, desc_index, language_id)
|
||||||
|
.map(|r| r.map_err(GetDescriptorError::Transfer))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
|
@ -206,7 +204,7 @@ impl Device {
|
||||||
},
|
},
|
||||||
timeout,
|
timeout,
|
||||||
)
|
)
|
||||||
.map(|r| Ok(r?))
|
.map(|r| r.map_err(GetDescriptorError::Transfer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,16 +216,13 @@ impl Device {
|
||||||
pub fn get_string_descriptor_supported_languages(
|
pub fn get_string_descriptor_supported_languages(
|
||||||
&self,
|
&self,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
) -> impl MaybeFuture<Output = Result<impl Iterator<Item = u16>, Error>> {
|
) -> impl MaybeFuture<Output = Result<impl Iterator<Item = u16>, GetDescriptorError>> {
|
||||||
self.get_descriptor(DESCRIPTOR_TYPE_STRING, 0, 0, timeout)
|
self.get_descriptor(DESCRIPTOR_TYPE_STRING, 0, 0, timeout)
|
||||||
.map(move |r| {
|
.map(move |r| {
|
||||||
let data = r?;
|
let data = r?;
|
||||||
if !validate_string_descriptor(&data) {
|
if !validate_string_descriptor(&data) {
|
||||||
error!("String descriptor language list read {data:?}, not a valid string descriptor");
|
error!("String descriptor language list read {data:?}, not a valid string descriptor");
|
||||||
return Err(Error::new(
|
return Err(GetDescriptorError::InvalidDescriptor)
|
||||||
ErrorKind::InvalidData,
|
|
||||||
"string descriptor data was invalid",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Use array_chunks once stable
|
//TODO: Use array_chunks once stable
|
||||||
|
|
@ -252,7 +247,7 @@ impl Device {
|
||||||
desc_index: NonZeroU8,
|
desc_index: NonZeroU8,
|
||||||
language_id: u16,
|
language_id: u16,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
) -> impl MaybeFuture<Output = Result<String, Error>> {
|
) -> impl MaybeFuture<Output = Result<String, GetDescriptorError>> {
|
||||||
self.get_descriptor(
|
self.get_descriptor(
|
||||||
DESCRIPTOR_TYPE_STRING,
|
DESCRIPTOR_TYPE_STRING,
|
||||||
desc_index.get(),
|
desc_index.get(),
|
||||||
|
|
@ -261,9 +256,7 @@ impl Device {
|
||||||
)
|
)
|
||||||
.map(|r| {
|
.map(|r| {
|
||||||
let data = r?;
|
let data = r?;
|
||||||
decode_string_descriptor(&data).map_err(|_| {
|
decode_string_descriptor(&data).map_err(|_| GetDescriptorError::InvalidDescriptor)
|
||||||
Error::new(ErrorKind::InvalidData, "string descriptor data was invalid")
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -500,16 +493,23 @@ impl Interface {
|
||||||
pub fn endpoint<EpType: EndpointType, Dir: EndpointDirection>(
|
pub fn endpoint<EpType: EndpointType, Dir: EndpointDirection>(
|
||||||
&self,
|
&self,
|
||||||
address: u8,
|
address: u8,
|
||||||
) -> Result<Endpoint<EpType, Dir>, ClaimEndpointError> {
|
) -> Result<Endpoint<EpType, Dir>, Error> {
|
||||||
let intf_desc = self.descriptor();
|
let intf_desc = self.descriptor();
|
||||||
let ep_desc =
|
let ep_desc =
|
||||||
intf_desc.and_then(|desc| desc.endpoints().find(|ep| ep.address() == address));
|
intf_desc.and_then(|desc| desc.endpoints().find(|ep| ep.address() == address));
|
||||||
let Some(ep_desc) = ep_desc else {
|
let Some(ep_desc) = ep_desc else {
|
||||||
return Err(ClaimEndpointError::InvalidAddress);
|
return Err(Error::new(
|
||||||
|
ErrorKind::NotFound,
|
||||||
|
"specified endpoint does not exist on this interface",
|
||||||
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
if ep_desc.transfer_type() != EpType::TYPE || address & Direction::MASK != Dir::DIR as u8 {
|
if address & Direction::MASK != Dir::DIR as u8 {
|
||||||
return Err(ClaimEndpointError::InvalidType);
|
return Err(Error::new(ErrorKind::Other, "incorrect endpoint direction"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ep_desc.transfer_type() != EpType::TYPE {
|
||||||
|
return Err(Error::new(ErrorKind::Other, "incorrect endpoint type"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let backend = self.backend.endpoint(ep_desc)?;
|
let backend = self.backend.endpoint(ep_desc)?;
|
||||||
|
|
@ -521,32 +521,6 @@ impl Interface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error from [`Interface::endpoint`].
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum ClaimEndpointError {
|
|
||||||
/// The specified address does not exist on this interface and alternate setting
|
|
||||||
InvalidAddress,
|
|
||||||
|
|
||||||
/// The type or direction does not match the endpoint descriptor for this address
|
|
||||||
InvalidType,
|
|
||||||
|
|
||||||
/// The endpoint is already claimed
|
|
||||||
Busy,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for ClaimEndpointError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
ClaimEndpointError::InvalidAddress => write!(f, "invalid endpoint address"),
|
|
||||||
ClaimEndpointError::InvalidType => write!(f, "incorrect endpoint type or direction"),
|
|
||||||
ClaimEndpointError::Busy => write!(f, "endpoint is already claimed"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for ClaimEndpointError {}
|
|
||||||
|
|
||||||
/// Exclusive access to an endpoint of a USB device.
|
/// Exclusive access to an endpoint of a USB device.
|
||||||
///
|
///
|
||||||
/// Obtain an `Endpoint` with the [`Interface::endpoint`] method.
|
/// Obtain an `Endpoint` with the [`Interface::endpoint`] method.
|
||||||
|
|
|
||||||
169
src/error.rs
Normal file
169
src/error.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
use std::{fmt::Display, io, num::NonZeroU32};
|
||||||
|
|
||||||
|
use crate::{platform::format_os_error_code, transfer::TransferError};
|
||||||
|
|
||||||
|
/// Error returned from `nusb` operations other than transfers.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Error {
|
||||||
|
pub(crate) kind: ErrorKind,
|
||||||
|
pub(crate) code: Option<NonZeroU32>,
|
||||||
|
pub(crate) message: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub(crate) fn new(kind: ErrorKind, message: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
code: None,
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
pub(crate) fn log_error(self) -> Self {
|
||||||
|
log::error!("{}", self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
pub(crate) fn log_debug(self) -> Self {
|
||||||
|
log::debug!("{}", self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the error kind.
|
||||||
|
pub fn kind(&self) -> ErrorKind {
|
||||||
|
self.kind
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the error code from the OS, if applicable.
|
||||||
|
///
|
||||||
|
/// * On Linux this is the `errno` value.
|
||||||
|
/// * On Windows this is the `WIN32_ERROR` value.
|
||||||
|
/// * On macOS this is the `IOReturn` value.
|
||||||
|
pub fn os_error(&self) -> Option<u32> {
|
||||||
|
self.code.map(|c| c.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.message)?;
|
||||||
|
if let Some(code) = self.code {
|
||||||
|
write!(f, " (")?;
|
||||||
|
format_os_error_code(f, code.get())?;
|
||||||
|
write!(f, ")")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
impl From<Error> for io::Error {
|
||||||
|
fn from(err: Error) -> Self {
|
||||||
|
let kind = match err.kind {
|
||||||
|
ErrorKind::Disconnected => io::ErrorKind::NotConnected,
|
||||||
|
ErrorKind::Busy => io::ErrorKind::Other, // TODO: ResourceBusy
|
||||||
|
ErrorKind::PermissionDenied => io::ErrorKind::PermissionDenied,
|
||||||
|
ErrorKind::NotFound => io::ErrorKind::NotFound,
|
||||||
|
ErrorKind::Unsupported => io::ErrorKind::Unsupported,
|
||||||
|
ErrorKind::Other => io::ErrorKind::Other,
|
||||||
|
};
|
||||||
|
io::Error::new(kind, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// General category of error as part of an [`Error`].
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
/// Device is disconnected.
|
||||||
|
Disconnected,
|
||||||
|
|
||||||
|
/// Device, interface, or endpoint is in use by another application, kernel driver, or handle.
|
||||||
|
Busy,
|
||||||
|
|
||||||
|
/// This user or application does not have permission to perform the requested operation.
|
||||||
|
PermissionDenied,
|
||||||
|
|
||||||
|
/// Requested configuration, interface, or alternate setting not found
|
||||||
|
NotFound,
|
||||||
|
|
||||||
|
/// The requested operation is not supported by the platform or its currently-configured driver.
|
||||||
|
Unsupported,
|
||||||
|
|
||||||
|
/// Uncategorized error.
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error from [`crate::Device::active_configuration`]
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct ActiveConfigurationError {
|
||||||
|
pub(crate) configuration_value: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ActiveConfigurationError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if self.configuration_value == 0 {
|
||||||
|
write!(f, "device is not configured")
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"no descriptor found for active configuration {}",
|
||||||
|
self.configuration_value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for ActiveConfigurationError {}
|
||||||
|
|
||||||
|
impl From<ActiveConfigurationError> for Error {
|
||||||
|
fn from(value: ActiveConfigurationError) -> Self {
|
||||||
|
let message = if value.configuration_value == 0 {
|
||||||
|
"device is not configured"
|
||||||
|
} else {
|
||||||
|
"no descriptor found for active configuration"
|
||||||
|
};
|
||||||
|
Error::new(ErrorKind::Other, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ActiveConfigurationError> for std::io::Error {
|
||||||
|
fn from(value: ActiveConfigurationError) -> Self {
|
||||||
|
std::io::Error::other(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error for descriptor reads.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum GetDescriptorError {
|
||||||
|
/// Transfer error when getting the descriptor.
|
||||||
|
Transfer(TransferError),
|
||||||
|
|
||||||
|
/// Invalid descriptor data
|
||||||
|
InvalidDescriptor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for GetDescriptorError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
GetDescriptorError::Transfer(e) => write!(f, "{}", e),
|
||||||
|
GetDescriptorError::InvalidDescriptor => write!(f, "invalid descriptor"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for GetDescriptorError {}
|
||||||
|
|
||||||
|
impl From<GetDescriptorError> for std::io::Error {
|
||||||
|
fn from(value: GetDescriptorError) -> Self {
|
||||||
|
match value {
|
||||||
|
GetDescriptorError::Transfer(e) => e.into(),
|
||||||
|
GetDescriptorError::InvalidDescriptor => {
|
||||||
|
std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid descriptor")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -134,8 +134,6 @@
|
||||||
//! These features do not affect and are not required for transfers, which are
|
//! These features do not affect and are not required for transfers, which are
|
||||||
//! implemented on top of natively-async OS APIs.
|
//! implemented on top of natively-async OS APIs.
|
||||||
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
mod platform;
|
mod platform;
|
||||||
|
|
||||||
pub mod descriptors;
|
pub mod descriptors;
|
||||||
|
|
@ -143,7 +141,7 @@ mod enumeration;
|
||||||
pub use enumeration::{BusInfo, DeviceId, DeviceInfo, InterfaceInfo, Speed, UsbControllerType};
|
pub use enumeration::{BusInfo, DeviceId, DeviceInfo, InterfaceInfo, Speed, UsbControllerType};
|
||||||
|
|
||||||
mod device;
|
mod device;
|
||||||
pub use device::{ClaimEndpointError, Device, Endpoint, Interface};
|
pub use device::{Device, Endpoint, Interface};
|
||||||
|
|
||||||
pub mod transfer;
|
pub mod transfer;
|
||||||
|
|
||||||
|
|
@ -154,8 +152,8 @@ pub use maybe_future::MaybeFuture;
|
||||||
|
|
||||||
mod bitset;
|
mod bitset;
|
||||||
|
|
||||||
/// OS error returned from operations other than transfers.
|
mod error;
|
||||||
pub type Error = io::Error;
|
pub use error::{ActiveConfigurationError, Error, ErrorKind, GetDescriptorError};
|
||||||
|
|
||||||
/// Get an iterator listing the connected devices.
|
/// Get an iterator listing the connected devices.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{
|
||||||
collections::{BTreeMap, VecDeque},
|
collections::{BTreeMap, VecDeque},
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{ErrorKind, Read, Seek},
|
io::{Read, Seek},
|
||||||
mem::ManuallyDrop,
|
mem::ManuallyDrop,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{
|
sync::{
|
||||||
|
|
@ -34,7 +34,6 @@ use crate::{
|
||||||
parse_concatenated_config_descriptors, ConfigurationDescriptor, DeviceDescriptor,
|
parse_concatenated_config_descriptors, ConfigurationDescriptor, DeviceDescriptor,
|
||||||
EndpointDescriptor, TransferType, DESCRIPTOR_LEN_DEVICE,
|
EndpointDescriptor, TransferType, DESCRIPTOR_LEN_DEVICE,
|
||||||
},
|
},
|
||||||
device::ClaimEndpointError,
|
|
||||||
maybe_future::{blocking::Blocking, MaybeFuture},
|
maybe_future::{blocking::Blocking, MaybeFuture},
|
||||||
transfer::{
|
transfer::{
|
||||||
internal::{
|
internal::{
|
||||||
|
|
@ -43,7 +42,7 @@ use crate::{
|
||||||
request_type, Buffer, Completion, ControlIn, ControlOut, ControlType, Direction, Recipient,
|
request_type, Buffer, Completion, ControlIn, ControlOut, ControlType, Direction, Recipient,
|
||||||
TransferError,
|
TransferError,
|
||||||
},
|
},
|
||||||
DeviceInfo, Error, Speed,
|
DeviceInfo, Error, ErrorKind, Speed,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
|
@ -82,7 +81,19 @@ impl LinuxDevice {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
let path = PathBuf::from(format!("/dev/bus/usb/{busnum:03}/{devnum:03}"));
|
let path = PathBuf::from(format!("/dev/bus/usb/{busnum:03}/{devnum:03}"));
|
||||||
let fd = rustix::fs::open(&path, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty())
|
let fd = rustix::fs::open(&path, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty())
|
||||||
.inspect_err(|e| warn!("Failed to open device {path:?}: {e}"))?;
|
.map_err(|e| {
|
||||||
|
match e {
|
||||||
|
Errno::NOENT => {
|
||||||
|
Error::new_os(ErrorKind::Disconnected, "device not found", e)
|
||||||
|
}
|
||||||
|
Errno::PERM => {
|
||||||
|
Error::new_os(ErrorKind::PermissionDenied, "permission denied", e)
|
||||||
|
}
|
||||||
|
e => Error::new_os(ErrorKind::Other, "failed to open device", e),
|
||||||
|
}
|
||||||
|
.log_debug()
|
||||||
|
})?;
|
||||||
|
|
||||||
Self::create_inner(fd, Some(sysfs_path))
|
Self::create_inner(fd, Some(sysfs_path))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -100,18 +111,18 @@ impl LinuxDevice {
|
||||||
fd: OwnedFd,
|
fd: OwnedFd,
|
||||||
sysfs: Option<SysfsPath>,
|
sysfs: Option<SysfsPath>,
|
||||||
) -> Result<Arc<LinuxDevice>, Error> {
|
) -> Result<Arc<LinuxDevice>, Error> {
|
||||||
let descriptors = read_all_from_fd(&fd)?;
|
let descriptors = read_all_from_fd(&fd).map_err(|e| {
|
||||||
|
Error::new_io(ErrorKind::Other, "failed to read descriptors", e).log_error()
|
||||||
|
})?;
|
||||||
|
|
||||||
let Some(_) = DeviceDescriptor::new(&descriptors) else {
|
let Some(_) = DeviceDescriptor::new(&descriptors) else {
|
||||||
return Err(Error::new(
|
return Err(Error::new(ErrorKind::Other, "invalid device descriptor"));
|
||||||
ErrorKind::InvalidData,
|
|
||||||
"invalid device descriptor",
|
|
||||||
));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let active_config: u8 = if let Some(sysfs) = sysfs.as_ref() {
|
let active_config: u8 = if let Some(sysfs) = sysfs.as_ref() {
|
||||||
sysfs.read_attr("bConfigurationValue").inspect_err(|e| {
|
sysfs.read_attr("bConfigurationValue").map_err(|e| {
|
||||||
warn!("failed to read sysfs bConfigurationValue: {e}");
|
warn!("failed to read sysfs bConfigurationValue: {e}");
|
||||||
|
Error::new(ErrorKind::Other, "failed to read sysfs bConfigurationValue")
|
||||||
})?
|
})?
|
||||||
} else {
|
} else {
|
||||||
request_configuration(&fd).unwrap_or_else(|()| {
|
request_configuration(&fd).unwrap_or_else(|()| {
|
||||||
|
|
@ -132,7 +143,7 @@ impl LinuxDevice {
|
||||||
rustix::time::TimerfdClockId::Monotonic,
|
rustix::time::TimerfdClockId::Monotonic,
|
||||||
TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK,
|
TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK,
|
||||||
)
|
)
|
||||||
.inspect_err(|e| log::error!("Failed to create timerfd: {e}"))?;
|
.map_err(|e| Error::new_os(ErrorKind::Other, "failed to create timerfd", e).log_error())?;
|
||||||
|
|
||||||
let arc = Arc::new_cyclic(|weak| {
|
let arc = Arc::new_cyclic(|weak| {
|
||||||
let events_id = DEVICES.lock().unwrap().insert(weak.clone());
|
let events_id = DEVICES.lock().unwrap().insert(weak.clone());
|
||||||
|
|
@ -313,7 +324,12 @@ impl LinuxDevice {
|
||||||
configuration: u8,
|
configuration: u8,
|
||||||
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
usbfs::set_configuration(&self.fd, configuration)?;
|
usbfs::set_configuration(&self.fd, configuration).map_err(|e| match e {
|
||||||
|
Errno::INVAL => Error::new_os(ErrorKind::NotFound, "configuration not found", e),
|
||||||
|
Errno::BUSY => Error::new_os(ErrorKind::Busy, "device is busy", e),
|
||||||
|
Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to set configuration", e),
|
||||||
|
})?;
|
||||||
self.active_config.store(configuration, Ordering::SeqCst);
|
self.active_config.store(configuration, Ordering::SeqCst);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
@ -321,8 +337,11 @@ impl LinuxDevice {
|
||||||
|
|
||||||
pub(crate) fn reset(self: Arc<Self>) -> impl MaybeFuture<Output = Result<(), Error>> {
|
pub(crate) fn reset(self: Arc<Self>) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
usbfs::reset(&self.fd)?;
|
usbfs::reset(&self.fd).map_err(|e| match e {
|
||||||
Ok(())
|
Errno::BUSY => Error::new_os(ErrorKind::Busy, "device is busy", e),
|
||||||
|
Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to reset device", e),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,16 +371,20 @@ impl LinuxDevice {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn claim_interface(
|
fn handle_claim_interface_result(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
interface_number: u8,
|
interface_number: u8,
|
||||||
) -> impl MaybeFuture<Output = Result<Arc<LinuxInterface>, Error>> {
|
result: Result<(), Errno>,
|
||||||
Blocking::new(move || {
|
reattach: bool,
|
||||||
usbfs::claim_interface(&self.fd, interface_number).inspect_err(|e| {
|
) -> Result<Arc<LinuxInterface>, Error> {
|
||||||
warn!(
|
result.map_err(|e| {
|
||||||
"Failed to claim interface {interface_number} on device id {dev}: {e}",
|
match e {
|
||||||
dev = self.events_id
|
Errno::INVAL => Error::new_os(ErrorKind::NotFound, "interface not found", e),
|
||||||
)
|
Errno::BUSY => Error::new_os(ErrorKind::Busy, "interface is busy", e),
|
||||||
|
Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to claim interface", e),
|
||||||
|
}
|
||||||
|
.log_error()
|
||||||
})?;
|
})?;
|
||||||
debug!(
|
debug!(
|
||||||
"Claimed interface {interface_number} on device id {dev}",
|
"Claimed interface {interface_number} on device id {dev}",
|
||||||
|
|
@ -370,9 +393,18 @@ impl LinuxDevice {
|
||||||
Ok(Arc::new(LinuxInterface {
|
Ok(Arc::new(LinuxInterface {
|
||||||
device: self,
|
device: self,
|
||||||
interface_number,
|
interface_number,
|
||||||
reattach: false,
|
reattach,
|
||||||
state: Mutex::new(Default::default()),
|
state: Mutex::new(Default::default()),
|
||||||
}))
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn claim_interface(
|
||||||
|
self: Arc<Self>,
|
||||||
|
interface_number: u8,
|
||||||
|
) -> impl MaybeFuture<Output = Result<Arc<LinuxInterface>, Error>> {
|
||||||
|
Blocking::new(move || {
|
||||||
|
let result = usbfs::claim_interface(&self.fd, interface_number);
|
||||||
|
self.handle_claim_interface_result(interface_number, result, false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -381,17 +413,8 @@ impl LinuxDevice {
|
||||||
interface_number: u8,
|
interface_number: u8,
|
||||||
) -> impl MaybeFuture<Output = Result<Arc<LinuxInterface>, Error>> {
|
) -> impl MaybeFuture<Output = Result<Arc<LinuxInterface>, Error>> {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
usbfs::detach_and_claim_interface(&self.fd, interface_number)?;
|
let result = usbfs::detach_and_claim_interface(&self.fd, interface_number);
|
||||||
debug!(
|
self.handle_claim_interface_result(interface_number, result, true)
|
||||||
"Detached and claimed interface {interface_number} on device id {dev}",
|
|
||||||
dev = self.events_id
|
|
||||||
);
|
|
||||||
Ok(Arc::new(LinuxInterface {
|
|
||||||
device: self,
|
|
||||||
interface_number,
|
|
||||||
reattach: true,
|
|
||||||
state: Mutex::new(Default::default()),
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -400,7 +423,12 @@ impl LinuxDevice {
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
interface_number: u8,
|
interface_number: u8,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
usbfs::detach_kernel_driver(&self.fd, interface_number).map_err(|e| e.into())
|
usbfs::detach_kernel_driver(&self.fd, interface_number).map_err(|e| match e {
|
||||||
|
Errno::INVAL => Error::new_os(ErrorKind::NotFound, "interface not found", e),
|
||||||
|
Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
|
||||||
|
Errno::NODATA => Error::new_os(ErrorKind::Other, "no kernel driver attached", e),
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to detach kernel driver", e),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
|
@ -408,7 +436,12 @@ impl LinuxDevice {
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
interface_number: u8,
|
interface_number: u8,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
usbfs::attach_kernel_driver(&self.fd, interface_number).map_err(|e| e.into())
|
usbfs::attach_kernel_driver(&self.fd, interface_number).map_err(|e| match e {
|
||||||
|
Errno::INVAL => Error::new_os(ErrorKind::NotFound, "interface not found", e),
|
||||||
|
Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
|
||||||
|
Errno::BUSY => Error::new_os(ErrorKind::Busy, "kernel driver already attached", e),
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to attach kernel driver", e),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn submit(&self, transfer: Idle<TransferData>) -> Pending<TransferData> {
|
pub(crate) fn submit(&self, transfer: Idle<TransferData>) -> Pending<TransferData> {
|
||||||
|
|
@ -583,16 +616,26 @@ impl LinuxInterface {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
if !state.endpoints.is_empty() {
|
if !state.endpoints.is_empty() {
|
||||||
// TODO: Use ErrorKind::ResourceBusy once compatible with MSRV
|
return Err(Error::new(
|
||||||
return Err(Error::other(
|
ErrorKind::Busy,
|
||||||
"must drop endpoints before changing alt setting",
|
"can't change alternate setting while endpoints are in use",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
usbfs::set_interface(&self.device.fd, self.interface_number, alt_setting).map_err(
|
||||||
|
|e| match e {
|
||||||
|
Errno::INVAL => {
|
||||||
|
Error::new_os(ErrorKind::NotFound, "alternate setting not found", e)
|
||||||
|
}
|
||||||
|
Errno::NODEV => {
|
||||||
|
Error::new_os(ErrorKind::Disconnected, "device disconnected", e)
|
||||||
|
}
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to set alternate setting", e),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
debug!(
|
debug!(
|
||||||
"Set interface {} alt setting to {alt_setting}",
|
"Set interface {} alt setting to {alt_setting}",
|
||||||
self.interface_number
|
self.interface_number
|
||||||
);
|
);
|
||||||
usbfs::set_interface(&self.device.fd, self.interface_number, alt_setting)?;
|
|
||||||
state.alt_setting = alt_setting;
|
state.alt_setting = alt_setting;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
@ -601,7 +644,7 @@ impl LinuxInterface {
|
||||||
pub fn endpoint(
|
pub fn endpoint(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
descriptor: EndpointDescriptor,
|
descriptor: EndpointDescriptor,
|
||||||
) -> Result<LinuxEndpoint, ClaimEndpointError> {
|
) -> Result<LinuxEndpoint, Error> {
|
||||||
let address = descriptor.address();
|
let address = descriptor.address();
|
||||||
let ep_type = descriptor.transfer_type();
|
let ep_type = descriptor.transfer_type();
|
||||||
let max_packet_size = descriptor.max_packet_size();
|
let max_packet_size = descriptor.max_packet_size();
|
||||||
|
|
@ -609,7 +652,7 @@ impl LinuxInterface {
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
if state.endpoints.is_set(address) {
|
if state.endpoints.is_set(address) {
|
||||||
return Err(ClaimEndpointError::Busy);
|
return Err(Error::new(ErrorKind::Busy, "endpoint already in use"));
|
||||||
}
|
}
|
||||||
state.endpoints.set(address);
|
state.endpoints.set(address);
|
||||||
|
|
||||||
|
|
@ -730,7 +773,10 @@ impl LinuxEndpoint {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
let endpoint = inner.address;
|
let endpoint = inner.address;
|
||||||
debug!("Clear halt, endpoint {endpoint:02x}");
|
debug!("Clear halt, endpoint {endpoint:02x}");
|
||||||
Ok(usbfs::clear_halt(&inner.interface.device.fd, endpoint)?)
|
usbfs::clear_halt(&inner.interface.device.fd, endpoint).map_err(|e| match e {
|
||||||
|
Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to clear halt", e),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use crate::enumeration::InterfaceInfo;
|
use crate::enumeration::InterfaceInfo;
|
||||||
use crate::maybe_future::{MaybeFuture, Ready};
|
use crate::maybe_future::{MaybeFuture, Ready};
|
||||||
|
use crate::ErrorKind;
|
||||||
use crate::{BusInfo, DeviceInfo, Error, Speed, UsbControllerType};
|
use crate::{BusInfo, DeviceInfo, Error, Speed, UsbControllerType};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -41,12 +42,6 @@ impl std::error::Error for SysfsError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SysfsError> for io::Error {
|
|
||||||
fn from(value: SysfsError) -> Self {
|
|
||||||
io::Error::other(Box::new(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SysfsPath {
|
impl SysfsPath {
|
||||||
fn parse_attr<T, E>(
|
fn parse_attr<T, E>(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -116,12 +111,24 @@ impl FromHexStr for u16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SYSFS_USB_PREFIX: &str = "/sys/bus/usb/devices/";
|
fn sysfs_list_usb() -> Result<fs::ReadDir, Error> {
|
||||||
|
fs::read_dir("/sys/bus/usb/devices/").map_err(|e| match e.kind() {
|
||||||
|
io::ErrorKind::NotFound => {
|
||||||
|
Error::new_io(ErrorKind::Other, "/sys/bus/usb/devices/ not found", e)
|
||||||
|
}
|
||||||
|
io::ErrorKind::PermissionDenied => Error::new_io(
|
||||||
|
ErrorKind::PermissionDenied,
|
||||||
|
"/sys/bus/usb/devices/ permission denied",
|
||||||
|
e,
|
||||||
|
),
|
||||||
|
_ => Error::new_io(ErrorKind::Other, "failed to open /sys/bus/usb/devices/", e),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn list_devices() -> impl MaybeFuture<Output = Result<impl Iterator<Item = DeviceInfo>, Error>>
|
pub fn list_devices() -> impl MaybeFuture<Output = Result<impl Iterator<Item = DeviceInfo>, Error>>
|
||||||
{
|
{
|
||||||
Ready((|| {
|
Ready((|| {
|
||||||
Ok(fs::read_dir(SYSFS_USB_PREFIX)?.flat_map(|entry| {
|
Ok(sysfs_list_usb()?.flat_map(|entry| {
|
||||||
let path = entry.ok()?.path();
|
let path = entry.ok()?.path();
|
||||||
let name = path.file_name()?;
|
let name = path.file_name()?;
|
||||||
|
|
||||||
|
|
@ -147,7 +154,7 @@ pub fn list_devices() -> impl MaybeFuture<Output = Result<impl Iterator<Item = D
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_root_hubs() -> Result<impl Iterator<Item = DeviceInfo>, Error> {
|
pub fn list_root_hubs() -> Result<impl Iterator<Item = DeviceInfo>, Error> {
|
||||||
Ok(fs::read_dir(SYSFS_USB_PREFIX)?.filter_map(|entry| {
|
Ok(sysfs_list_usb()?.filter_map(|entry| {
|
||||||
let path = entry.ok()?.path();
|
let path = entry.ok()?.path();
|
||||||
let name = path.file_name()?;
|
let name = path.file_name()?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
//! on a device use the same file descriptor, putting USB-specific
|
//! on a device use the same file descriptor, putting USB-specific
|
||||||
//! dispatch in the event loop avoids additonal synchronization.
|
//! dispatch in the event loop avoids additonal synchronization.
|
||||||
|
|
||||||
use crate::Error;
|
use crate::{Error, ErrorKind};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use rustix::{
|
use rustix::{
|
||||||
event::epoll::{self, EventData, EventFlags},
|
event::epoll::{self, EventData, EventFlags},
|
||||||
|
|
@ -19,7 +19,7 @@ use rustix::{
|
||||||
io::Errno,
|
io::Errno,
|
||||||
};
|
};
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use std::{io, mem::MaybeUninit, sync::Mutex, task::Waker, thread};
|
use std::{mem::MaybeUninit, sync::Mutex, task::Waker, thread};
|
||||||
|
|
||||||
use super::Device;
|
use super::Device;
|
||||||
|
|
||||||
|
|
@ -61,8 +61,8 @@ pub(super) fn register_fd(fd: BorrowedFd, tag: Tag, flags: EventFlags) -> Result
|
||||||
let mut start_thread = false;
|
let mut start_thread = false;
|
||||||
let epoll_fd = EPOLL_FD.get_or_try_init(|| {
|
let epoll_fd = EPOLL_FD.get_or_try_init(|| {
|
||||||
start_thread = true;
|
start_thread = true;
|
||||||
epoll::create(epoll::CreateFlags::CLOEXEC).inspect_err(|e| {
|
epoll::create(epoll::CreateFlags::CLOEXEC).map_err(|e| {
|
||||||
log::error!("Failed to initialize epoll: {e}");
|
Error::new_os(ErrorKind::Other, "failed to initialize epoll", e).log_error()
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
@ -70,9 +70,8 @@ pub(super) fn register_fd(fd: BorrowedFd, tag: Tag, flags: EventFlags) -> Result
|
||||||
thread::spawn(event_loop);
|
thread::spawn(event_loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
epoll::add(epoll_fd, fd, tag.as_event_data(), flags).inspect_err(|e| {
|
epoll::add(epoll_fd, fd, tag.as_event_data(), flags)
|
||||||
log::error!("Failed to add epoll watch: {e}");
|
.map_err(|e| Error::new_os(ErrorKind::Other, "failed to add epoll watch", e).log_error())?;
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -115,13 +114,13 @@ pub(crate) struct Async<T: AsFd> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsFd> Async<T> {
|
impl<T: AsFd> Async<T> {
|
||||||
pub fn new(inner: T) -> Result<Self, io::Error> {
|
pub fn new(inner: T) -> Result<Self, Error> {
|
||||||
let id = WAKERS.lock().unwrap().insert(None);
|
let id = WAKERS.lock().unwrap().insert(None);
|
||||||
register_fd(inner.as_fd(), Tag::Waker(id), EventFlags::empty())?;
|
register_fd(inner.as_fd(), Tag::Waker(id), EventFlags::empty())?;
|
||||||
Ok(Async { inner, id })
|
Ok(Async { inner, id })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(&self, waker: &Waker) -> Result<(), io::Error> {
|
pub fn register(&self, waker: &Waker) -> Result<(), Error> {
|
||||||
WAKERS
|
WAKERS
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
@ -134,7 +133,10 @@ impl<T: AsFd> Async<T> {
|
||||||
self.inner.as_fd(),
|
self.inner.as_fd(),
|
||||||
Tag::Waker(self.id).as_event_data(),
|
Tag::Waker(self.id).as_event_data(),
|
||||||
EventFlags::ONESHOT | EventFlags::IN,
|
EventFlags::ONESHOT | EventFlags::IN,
|
||||||
)?;
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::new_os(ErrorKind::Other, "failed to modify epoll watch", e).log_error()
|
||||||
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use rustix::{
|
||||||
};
|
};
|
||||||
use std::{mem::MaybeUninit, os::unix::prelude::BorrowedFd, path::Path, task::Poll};
|
use std::{mem::MaybeUninit, os::unix::prelude::BorrowedFd, path::Path, task::Poll};
|
||||||
|
|
||||||
use crate::{hotplug::HotplugEvent, Error};
|
use crate::{hotplug::HotplugEvent, Error, ErrorKind};
|
||||||
|
|
||||||
use super::{enumeration::probe_device, events::Async, SysfsPath};
|
use super::{enumeration::probe_device, events::Async, SysfsPath};
|
||||||
|
|
||||||
|
|
@ -28,8 +28,15 @@ impl LinuxHotplugWatch {
|
||||||
SocketType::RAW,
|
SocketType::RAW,
|
||||||
SocketFlags::CLOEXEC,
|
SocketFlags::CLOEXEC,
|
||||||
Some(netlink::KOBJECT_UEVENT),
|
Some(netlink::KOBJECT_UEVENT),
|
||||||
)?;
|
)
|
||||||
bind(&fd, &SocketAddrNetlink::new(0, UDEV_MULTICAST_GROUP))?;
|
.map_err(|e| {
|
||||||
|
Error::new_os(ErrorKind::Other, "failed to open udev netlink socket", e).log_error()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
bind(&fd, &SocketAddrNetlink::new(0, UDEV_MULTICAST_GROUP)).map_err(|e| {
|
||||||
|
Error::new_os(ErrorKind::Other, "failed to bind udev netlink socket", e).log_error()
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(LinuxHotplugWatch {
|
Ok(LinuxHotplugWatch {
|
||||||
fd: Async::new(fd)?,
|
fd: Async::new(fd)?,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
mod transfer;
|
mod transfer;
|
||||||
|
use std::io;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
use rustix::io::Errno;
|
use rustix::io::Errno;
|
||||||
pub(crate) use transfer::TransferData;
|
pub(crate) use transfer::TransferData;
|
||||||
mod usbfs;
|
mod usbfs;
|
||||||
|
|
@ -16,6 +19,7 @@ mod hotplug;
|
||||||
pub(crate) use hotplug::LinuxHotplugWatch as HotplugWatch;
|
pub(crate) use hotplug::LinuxHotplugWatch as HotplugWatch;
|
||||||
|
|
||||||
use crate::transfer::TransferError;
|
use crate::transfer::TransferError;
|
||||||
|
use crate::ErrorKind;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct DeviceId {
|
pub struct DeviceId {
|
||||||
|
|
@ -35,3 +39,25 @@ fn errno_to_transfer_error(e: Errno) -> TransferError {
|
||||||
_ => TransferError::Unknown(e.raw_os_error() as u32),
|
_ => TransferError::Unknown(e.raw_os_error() as u32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format_os_error_code(f: &mut std::fmt::Formatter<'_>, code: u32) -> std::fmt::Result {
|
||||||
|
write!(f, "errno {}", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::error::Error {
|
||||||
|
pub(crate) fn new_os(kind: ErrorKind, message: &'static str, code: Errno) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
code: NonZeroU32::new(code.raw_os_error() as u32),
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_io(kind: ErrorKind, message: &'static str, err: io::Error) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
code: err.raw_os_error().and_then(|i| NonZeroU32::new(i as u32)),
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
io::ErrorKind,
|
|
||||||
mem::ManuallyDrop,
|
mem::ManuallyDrop,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicU8, AtomicUsize, Ordering},
|
atomic::{AtomicU8, AtomicUsize, Ordering},
|
||||||
|
|
@ -17,7 +16,6 @@ use log::{debug, error};
|
||||||
use crate::{
|
use crate::{
|
||||||
bitset::EndpointBitSet,
|
bitset::EndpointBitSet,
|
||||||
descriptors::{ConfigurationDescriptor, DeviceDescriptor, EndpointDescriptor},
|
descriptors::{ConfigurationDescriptor, DeviceDescriptor, EndpointDescriptor},
|
||||||
device::ClaimEndpointError,
|
|
||||||
maybe_future::blocking::Blocking,
|
maybe_future::blocking::Blocking,
|
||||||
transfer::{
|
transfer::{
|
||||||
internal::{
|
internal::{
|
||||||
|
|
@ -25,13 +23,13 @@ use crate::{
|
||||||
},
|
},
|
||||||
Buffer, Completion, ControlIn, ControlOut, Direction, TransferError,
|
Buffer, Completion, ControlIn, ControlOut, Direction, TransferError,
|
||||||
},
|
},
|
||||||
DeviceInfo, Error, MaybeFuture, Speed,
|
DeviceInfo, Error, ErrorKind, MaybeFuture, Speed,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
enumeration::{device_descriptor_from_fields, get_integer_property, service_by_registry_id},
|
enumeration::{device_descriptor_from_fields, get_integer_property, service_by_registry_id},
|
||||||
events::{add_event_source, EventRegistration},
|
events::{add_event_source, EventRegistration},
|
||||||
iokit::{call_iokit_function, check_iokit_return},
|
iokit::call_iokit_function,
|
||||||
iokit_c::IOUSBDevRequestTO,
|
iokit_c::IOUSBDevRequestTO,
|
||||||
iokit_usb::{IoKitDevice, IoKitInterface},
|
iokit_usb::{IoKitDevice, IoKitInterface},
|
||||||
TransferData,
|
TransferData,
|
||||||
|
|
@ -70,23 +68,32 @@ impl MacDevice {
|
||||||
log::info!("Opening device from registry id {}", registry_id);
|
log::info!("Opening device from registry id {}", registry_id);
|
||||||
let service = service_by_registry_id(registry_id)?;
|
let service = service_by_registry_id(registry_id)?;
|
||||||
let device = IoKitDevice::new(&service)?;
|
let device = IoKitDevice::new(&service)?;
|
||||||
let _event_registration = add_event_source(device.create_async_event_source()?);
|
let event_source = device.create_async_event_source().map_err(|e| {
|
||||||
|
Error::new_os(ErrorKind::Other, "failed to create async event source", e)
|
||||||
|
.log_error()
|
||||||
|
})?;
|
||||||
|
let _event_registration = add_event_source(event_source);
|
||||||
|
|
||||||
let opened = match unsafe { call_iokit_function!(device.raw, USBDeviceOpen()) } {
|
let opened = device
|
||||||
io_kit_sys::ret::kIOReturnSuccess => true,
|
.open()
|
||||||
err => {
|
.inspect_err(|err| {
|
||||||
// Most methods don't require USBDeviceOpen() so this can be ignored
|
log::debug!("Could not open device for exclusive access: 0x{err:08x}");
|
||||||
// to allow different processes to open different interfaces.
|
})
|
||||||
log::debug!("Could not open device for exclusive access: {err:x}");
|
.is_ok();
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let device_descriptor = device_descriptor_from_fields(&service)
|
let device_descriptor = device_descriptor_from_fields(&service).ok_or_else(|| {
|
||||||
.ok_or_else(|| Error::other("could not read properties for device descriptor"))?;
|
Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"could not read properties for device descriptor",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let num_configs = device.get_number_of_configurations().inspect_err(|e| {
|
let num_configs = device.get_number_of_configurations().map_err(|e| {
|
||||||
log::warn!("failed to get number of configurations: {e}");
|
Error::new_os(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"failed to get number of configurations",
|
||||||
|
e,
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let config_descriptors: Vec<Vec<u8>> = (0..num_configs)
|
let config_descriptors: Vec<Vec<u8>> = (0..num_configs)
|
||||||
|
|
@ -149,12 +156,22 @@ impl MacDevice {
|
||||||
fn require_open_exclusive(&self) -> Result<(), Error> {
|
fn require_open_exclusive(&self) -> Result<(), Error> {
|
||||||
let mut is_open_exclusive = self.is_open_exclusive.lock().unwrap();
|
let mut is_open_exclusive = self.is_open_exclusive.lock().unwrap();
|
||||||
if !*is_open_exclusive {
|
if !*is_open_exclusive {
|
||||||
unsafe { check_iokit_return(call_iokit_function!(self.device.raw, USBDeviceOpen()))? };
|
self.device.open().map_err(|e| match e {
|
||||||
|
io_kit_sys::ret::kIOReturnNoDevice => {
|
||||||
|
Error::new_os(ErrorKind::Disconnected, "device disconnected", e)
|
||||||
|
}
|
||||||
|
_ => Error::new_os(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"could not open device for exclusive access",
|
||||||
|
e,
|
||||||
|
),
|
||||||
|
})?;
|
||||||
*is_open_exclusive = true;
|
*is_open_exclusive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.claimed_interfaces.load(Ordering::Relaxed) != 0 {
|
if self.claimed_interfaces.load(Ordering::Relaxed) != 0 {
|
||||||
return Err(Error::other(
|
return Err(Error::new(
|
||||||
|
ErrorKind::Busy,
|
||||||
"cannot perform this operation while interfaces are claimed",
|
"cannot perform this operation while interfaces are claimed",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -168,12 +185,17 @@ impl MacDevice {
|
||||||
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
self.require_open_exclusive()?;
|
self.require_open_exclusive()?;
|
||||||
unsafe {
|
self.device
|
||||||
check_iokit_return(call_iokit_function!(
|
.set_configuration(configuration)
|
||||||
self.device.raw,
|
.map_err(|e| match e {
|
||||||
SetConfiguration(configuration)
|
io_kit_sys::ret::kIOReturnNoDevice => {
|
||||||
))?
|
Error::new_os(ErrorKind::Disconnected, "device disconnected", e)
|
||||||
}
|
}
|
||||||
|
io_kit_sys::ret::kIOReturnNotFound => {
|
||||||
|
Error::new_os(ErrorKind::NotFound, "configuration not found", e)
|
||||||
|
}
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to set configuration", e),
|
||||||
|
})?;
|
||||||
log::debug!("Set configuration {configuration}");
|
log::debug!("Set configuration {configuration}");
|
||||||
self.active_config.store(configuration, Ordering::SeqCst);
|
self.active_config.store(configuration, Ordering::SeqCst);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -183,12 +205,12 @@ impl MacDevice {
|
||||||
pub(crate) fn reset(self: Arc<Self>) -> impl MaybeFuture<Output = Result<(), Error>> {
|
pub(crate) fn reset(self: Arc<Self>) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
self.require_open_exclusive()?;
|
self.require_open_exclusive()?;
|
||||||
unsafe {
|
self.device.reset().map_err(|e| match e {
|
||||||
check_iokit_return(call_iokit_function!(
|
io_kit_sys::ret::kIOReturnNoDevice => {
|
||||||
self.device.raw,
|
Error::new_os(ErrorKind::Disconnected, "device disconnected", e)
|
||||||
USBDeviceReEnumerate(0)
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to reset device", e),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,7 +221,10 @@ impl MacDevice {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
let intf_service = self
|
let intf_service = self
|
||||||
.device
|
.device
|
||||||
.create_interface_iterator()?
|
.create_interface_iterator()
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::new_os(ErrorKind::Other, "failed to create interface iterator", e)
|
||||||
|
})?
|
||||||
.find(|io_service| {
|
.find(|io_service| {
|
||||||
get_integer_property(io_service, "bInterfaceNumber")
|
get_integer_property(io_service, "bInterfaceNumber")
|
||||||
== Some(interface_number as i64)
|
== Some(interface_number as i64)
|
||||||
|
|
@ -207,9 +232,23 @@ impl MacDevice {
|
||||||
.ok_or(Error::new(ErrorKind::NotFound, "interface not found"))?;
|
.ok_or(Error::new(ErrorKind::NotFound, "interface not found"))?;
|
||||||
|
|
||||||
let mut interface = IoKitInterface::new(intf_service)?;
|
let mut interface = IoKitInterface::new(intf_service)?;
|
||||||
let _event_registration = add_event_source(interface.create_async_event_source()?);
|
let source = interface.create_async_event_source().map_err(|e| {
|
||||||
|
Error::new_os(ErrorKind::Other, "failed to create async event source", e)
|
||||||
|
.log_error()
|
||||||
|
})?;
|
||||||
|
let _event_registration = add_event_source(source);
|
||||||
|
|
||||||
interface.open()?;
|
interface.open().map_err(|e| match e {
|
||||||
|
io_kit_sys::ret::kIOReturnExclusiveAccess => Error::new_os(
|
||||||
|
ErrorKind::Busy,
|
||||||
|
"could not open interface for exclusive access",
|
||||||
|
e,
|
||||||
|
),
|
||||||
|
io_kit_sys::ret::kIOReturnNoDevice => {
|
||||||
|
Error::new_os(ErrorKind::Disconnected, "device disconnected", e)
|
||||||
|
}
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to open interface", e),
|
||||||
|
})?;
|
||||||
self.claimed_interfaces.fetch_add(1, Ordering::Acquire);
|
self.claimed_interfaces.fetch_add(1, Ordering::Acquire);
|
||||||
|
|
||||||
Ok(Arc::new(MacInterface {
|
Ok(Arc::new(MacInterface {
|
||||||
|
|
@ -357,19 +396,20 @@ impl MacInterface {
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
if !state.endpoints_used.is_empty() {
|
if !state.endpoints_used.is_empty() {
|
||||||
// TODO: Use ErrorKind::ResourceBusy once compatible with MSRV
|
return Err(Error::new(
|
||||||
|
ErrorKind::Busy,
|
||||||
return Err(Error::other(
|
"can't change alternate setting while endpoints are in use",
|
||||||
"must drop endpoints before changing alt setting",
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
self.interface
|
||||||
check_iokit_return(call_iokit_function!(
|
.set_alternate_interface(alt_setting)
|
||||||
self.interface.raw,
|
.map_err(|e| match e {
|
||||||
SetAlternateInterface(alt_setting)
|
io_kit_sys::ret::kIOReturnNoDevice => {
|
||||||
))?;
|
Error::new_os(ErrorKind::Disconnected, "device disconnected", e)
|
||||||
}
|
}
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to set alternate interface", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Set interface {} alt setting to {alt_setting}",
|
"Set interface {} alt setting to {alt_setting}",
|
||||||
|
|
@ -405,7 +445,7 @@ impl MacInterface {
|
||||||
pub fn endpoint(
|
pub fn endpoint(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
descriptor: EndpointDescriptor,
|
descriptor: EndpointDescriptor,
|
||||||
) -> Result<MacEndpoint, ClaimEndpointError> {
|
) -> Result<MacEndpoint, Error> {
|
||||||
let address = descriptor.address();
|
let address = descriptor.address();
|
||||||
let max_packet_size = descriptor.max_packet_size();
|
let max_packet_size = descriptor.max_packet_size();
|
||||||
|
|
||||||
|
|
@ -413,11 +453,14 @@ impl MacInterface {
|
||||||
|
|
||||||
let Some(pipe_ref) = self.interface.find_pipe_ref(address) else {
|
let Some(pipe_ref) = self.interface.find_pipe_ref(address) else {
|
||||||
debug!("Endpoint {address:02X} not found in iokit");
|
debug!("Endpoint {address:02X} not found in iokit");
|
||||||
return Err(ClaimEndpointError::InvalidAddress);
|
return Err(Error::new(
|
||||||
|
ErrorKind::NotFound,
|
||||||
|
"specified endpoint does not exist on IOKit interface",
|
||||||
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
if state.endpoints_used.is_set(address) {
|
if state.endpoints_used.is_set(address) {
|
||||||
return Err(ClaimEndpointError::Busy);
|
return Err(Error::new(ErrorKind::Busy, "endpoint already in use"));
|
||||||
}
|
}
|
||||||
state.endpoints_used.set(address);
|
state.endpoints_used.set(address);
|
||||||
|
|
||||||
|
|
@ -589,12 +632,16 @@ impl MacEndpoint {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
debug!("Clear halt, endpoint {:02x}", inner.address);
|
debug!("Clear halt, endpoint {:02x}", inner.address);
|
||||||
|
|
||||||
unsafe {
|
inner
|
||||||
check_iokit_return(call_iokit_function!(
|
.interface
|
||||||
inner.interface.interface.raw,
|
.interface
|
||||||
ClearPipeStallBothEnds(inner.pipe_ref)
|
.clear_pipe_stall_both_ends(inner.pipe_ref)
|
||||||
))
|
.map_err(|e| match e {
|
||||||
|
io_kit_sys::ret::kIOReturnNoDevice => {
|
||||||
|
Error::new_os(ErrorKind::Disconnected, "device disconnected", e)
|
||||||
}
|
}
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "failed to clear halt on endpoint", e),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
use std::io::ErrorKind;
|
|
||||||
|
|
||||||
use core_foundation::{
|
use core_foundation::{
|
||||||
base::{CFType, TCFType},
|
base::{CFType, TCFType},
|
||||||
data::CFData,
|
data::CFData,
|
||||||
|
|
@ -18,7 +16,7 @@ use log::debug;
|
||||||
use crate::{
|
use crate::{
|
||||||
descriptors::DeviceDescriptor,
|
descriptors::DeviceDescriptor,
|
||||||
maybe_future::{MaybeFuture, Ready},
|
maybe_future::{MaybeFuture, Ready},
|
||||||
BusInfo, DeviceInfo, Error, InterfaceInfo, Speed, UsbControllerType,
|
BusInfo, DeviceInfo, Error, ErrorKind, InterfaceInfo, Speed, UsbControllerType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::iokit::{IoService, IoServiceIterator};
|
use super::iokit::{IoService, IoServiceIterator};
|
||||||
|
|
@ -43,13 +41,17 @@ fn usb_service_iter() -> Result<IoServiceIterator, Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let dictionary = IOServiceMatching(kIOUSBDeviceClassName);
|
let dictionary = IOServiceMatching(kIOUSBDeviceClassName);
|
||||||
if dictionary.is_null() {
|
if dictionary.is_null() {
|
||||||
return Err(Error::other("IOServiceMatching failed"));
|
return Err(Error::new(ErrorKind::Other, "IOServiceMatching failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut iterator = 0;
|
let mut iterator = 0;
|
||||||
let r = IOServiceGetMatchingServices(kIOMasterPortDefault, dictionary, &mut iterator);
|
let r = IOServiceGetMatchingServices(kIOMasterPortDefault, dictionary, &mut iterator);
|
||||||
if r != kIOReturnSuccess {
|
if r != kIOReturnSuccess {
|
||||||
return Err(Error::from_raw_os_error(r));
|
return Err(Error::new_os(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"failed to create IOKit iterator",
|
||||||
|
r,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(IoServiceIterator::new(iterator))
|
Ok(IoServiceIterator::new(iterator))
|
||||||
|
|
@ -67,13 +69,17 @@ fn usb_controller_service_iter(
|
||||||
UsbControllerType::VHCI => IOServiceMatching(kAppleUSBVHCI),
|
UsbControllerType::VHCI => IOServiceMatching(kAppleUSBVHCI),
|
||||||
};
|
};
|
||||||
if dictionary.is_null() {
|
if dictionary.is_null() {
|
||||||
return Err(Error::other("IOServiceMatching failed"));
|
return Err(Error::new(ErrorKind::Other, "IOServiceMatching failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut iterator = 0;
|
let mut iterator = 0;
|
||||||
let r = IOServiceGetMatchingServices(kIOMasterPortDefault, dictionary, &mut iterator);
|
let r = IOServiceGetMatchingServices(kIOMasterPortDefault, dictionary, &mut iterator);
|
||||||
if r != kIOReturnSuccess {
|
if r != kIOReturnSuccess {
|
||||||
return Err(Error::from_raw_os_error(r));
|
return Err(Error::new_os(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"failed to create IOKit iterator",
|
||||||
|
r,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(IoServiceIterator::new(iterator))
|
Ok(IoServiceIterator::new(iterator))
|
||||||
|
|
@ -245,7 +251,11 @@ fn get_children(device: &IoService) -> Result<IoServiceIterator, Error> {
|
||||||
IORegistryEntryGetChildIterator(device.get(), kIOServicePlane as *mut _, &mut iterator);
|
IORegistryEntryGetChildIterator(device.get(), kIOServicePlane as *mut _, &mut iterator);
|
||||||
if r != kIOReturnSuccess {
|
if r != kIOReturnSuccess {
|
||||||
debug!("IORegistryEntryGetChildIterator failed: {r}");
|
debug!("IORegistryEntryGetChildIterator failed: {r}");
|
||||||
return Err(Error::from_raw_os_error(r));
|
return Err(Error::new_os(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"failed to create IOKit child iterator",
|
||||||
|
r,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(IoServiceIterator::new(iterator))
|
Ok(IoServiceIterator::new(iterator))
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ use io_kit_sys::{
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
|
||||||
use crate::{hotplug::HotplugEvent, DeviceId, Error};
|
use crate::{hotplug::HotplugEvent, DeviceId, Error, ErrorKind};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
enumeration::{get_registry_id, probe_device},
|
enumeration::{get_registry_id, probe_device},
|
||||||
|
|
@ -79,7 +79,7 @@ impl MacHotplugWatch {
|
||||||
let dictionary = unsafe {
|
let dictionary = unsafe {
|
||||||
let d = IOServiceMatching(kIOUSBDeviceClassName);
|
let d = IOServiceMatching(kIOUSBDeviceClassName);
|
||||||
if d.is_null() {
|
if d.is_null() {
|
||||||
return Err(Error::other("IOServiceMatching failed"));
|
return Err(Error::new(ErrorKind::Other, "IOServiceMatching failed"));
|
||||||
}
|
}
|
||||||
CFDictionary::wrap_under_create_rule(d)
|
CFDictionary::wrap_under_create_rule(d)
|
||||||
};
|
};
|
||||||
|
|
@ -162,7 +162,9 @@ fn register_notification(
|
||||||
);
|
);
|
||||||
|
|
||||||
if r != kIOReturnSuccess {
|
if r != kIOReturnSuccess {
|
||||||
return Err(Error::other("Failed to register notification"));
|
return Err(
|
||||||
|
Error::new_os(ErrorKind::Other, "failed to register notification", r).log_error(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let mut iter = IoServiceIterator::new(iter);
|
let mut iter = IoServiceIterator::new(iter);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,6 @@
|
||||||
|
|
||||||
use core_foundation_sys::uuid::CFUUIDBytes;
|
use core_foundation_sys::uuid::CFUUIDBytes;
|
||||||
use io_kit_sys::{ret::IOReturn, IOIteratorNext, IOObjectRelease};
|
use io_kit_sys::{ret::IOReturn, IOIteratorNext, IOObjectRelease};
|
||||||
use std::io::ErrorKind;
|
|
||||||
|
|
||||||
use crate::Error;
|
|
||||||
|
|
||||||
use super::iokit_c::{self, CFUUIDGetUUIDBytes, IOCFPlugInInterface};
|
use super::iokit_c::{self, CFUUIDGetUUIDBytes, IOCFPlugInInterface};
|
||||||
|
|
||||||
|
|
@ -117,15 +114,9 @@ pub(crate) fn usb_interface_type_id() -> CFUUIDBytes {
|
||||||
unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBInterfaceInterfaceID500()) }
|
unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBInterfaceInterfaceID500()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn check_iokit_return(r: IOReturn) -> Result<(), Error> {
|
pub(crate) fn check_iokit_return(r: IOReturn) -> Result<(), IOReturn> {
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
#[deny(unreachable_patterns)]
|
|
||||||
match r {
|
match r {
|
||||||
io_kit_sys::ret::kIOReturnSuccess => Ok(()),
|
io_kit_sys::ret::kIOReturnSuccess => Ok(()),
|
||||||
io_kit_sys::ret::kIOReturnExclusiveAccess => {
|
e => Err(e),
|
||||||
Err(Error::other("could not be opened for exclusive access"))
|
|
||||||
}
|
|
||||||
io_kit_sys::ret::kIOReturnNotFound => Err(Error::new(ErrorKind::NotFound, "not found")),
|
|
||||||
_ => Err(Error::from_raw_os_error(r)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
//! Based on Kate Temkin's [usrs](https://github.com/ktemkin/usrs)
|
//! Based on Kate Temkin's [usrs](https://github.com/ktemkin/usrs)
|
||||||
//! licensed under MIT OR Apache-2.0.
|
//! licensed under MIT OR Apache-2.0.
|
||||||
|
|
||||||
use std::{io::ErrorKind, ptr, slice, time::Duration};
|
use std::{ptr, slice, time::Duration};
|
||||||
|
|
||||||
use core_foundation::{base::TCFType, runloop::CFRunLoopSource};
|
use core_foundation::{base::TCFType, runloop::CFRunLoopSource};
|
||||||
use core_foundation_sys::runloop::CFRunLoopSourceRef;
|
use core_foundation_sys::runloop::CFRunLoopSourceRef;
|
||||||
use io_kit_sys::{
|
use io_kit_sys::{
|
||||||
ret::{kIOReturnNoResources, kIOReturnSuccess},
|
ret::{kIOReturnNoResources, kIOReturnSuccess, IOReturn},
|
||||||
types::io_iterator_t,
|
types::io_iterator_t,
|
||||||
};
|
};
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
@ -17,7 +17,7 @@ use crate::{
|
||||||
platform::macos_iokit::{
|
platform::macos_iokit::{
|
||||||
iokit::usb_interface_type_id, iokit_c::kIOUsbInterfaceUserClientTypeID,
|
iokit::usb_interface_type_id, iokit_c::kIOUsbInterfaceUserClientTypeID,
|
||||||
},
|
},
|
||||||
Error,
|
Error, ErrorKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
|
@ -75,12 +75,15 @@ impl IoKitDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
if rc != kIOReturnSuccess {
|
if rc != kIOReturnSuccess {
|
||||||
return Err(Error::from_raw_os_error(rc));
|
return Err(Error::new_os(ErrorKind::Other, "failed to open device", rc));
|
||||||
}
|
}
|
||||||
|
|
||||||
if raw_device_plugin.is_null() {
|
if raw_device_plugin.is_null() {
|
||||||
error!("IOKit indicated it successfully created a PlugInInterface, but the pointer was NULL");
|
error!("IOKit indicated it successfully created a PlugInInterface, but the pointer was NULL");
|
||||||
return Err(Error::other("Could not create PlugInInterface"));
|
return Err(Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"could not create PlugInInterface",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let device_plugin = PluginInterface::new(raw_device_plugin);
|
let device_plugin = PluginInterface::new(raw_device_plugin);
|
||||||
|
|
@ -113,7 +116,24 @@ impl IoKitDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn create_async_event_source(&self) -> Result<CFRunLoopSource, Error> {
|
pub(crate) fn open(&self) -> Result<(), IOReturn> {
|
||||||
|
unsafe { check_iokit_return(call_iokit_function!(self.raw, USBDeviceOpen())) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_configuration(&self, configuration: u8) -> Result<(), IOReturn> {
|
||||||
|
unsafe {
|
||||||
|
check_iokit_return(call_iokit_function!(
|
||||||
|
self.raw,
|
||||||
|
SetConfiguration(configuration)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reset(&self) -> Result<(), IOReturn> {
|
||||||
|
unsafe { check_iokit_return(call_iokit_function!(self.raw, USBDeviceReEnumerate(0))) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn create_async_event_source(&self) -> Result<CFRunLoopSource, IOReturn> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut raw_source: CFRunLoopSourceRef = std::ptr::null_mut();
|
let mut raw_source: CFRunLoopSourceRef = std::ptr::null_mut();
|
||||||
check_iokit_return(call_iokit_function!(
|
check_iokit_return(call_iokit_function!(
|
||||||
|
|
@ -125,7 +145,7 @@ impl IoKitDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an IOKit iterator that can be used to iterate over all interfaces on this device.
|
/// Returns an IOKit iterator that can be used to iterate over all interfaces on this device.
|
||||||
pub(crate) fn create_interface_iterator(&self) -> Result<IoServiceIterator, Error> {
|
pub(crate) fn create_interface_iterator(&self) -> Result<IoServiceIterator, IOReturn> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut iterator: io_iterator_t = 0;
|
let mut iterator: io_iterator_t = 0;
|
||||||
|
|
||||||
|
|
@ -145,7 +165,7 @@ impl IoKitDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_number_of_configurations(&self) -> Result<u8, Error> {
|
pub(crate) fn get_number_of_configurations(&self) -> Result<u8, IOReturn> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut num = 0;
|
let mut num = 0;
|
||||||
check_iokit_return(call_iokit_function!(
|
check_iokit_return(call_iokit_function!(
|
||||||
|
|
@ -156,7 +176,7 @@ impl IoKitDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_configuration_descriptor(&self, index: u8) -> Result<&[u8], Error> {
|
pub(crate) fn get_configuration_descriptor(&self, index: u8) -> Result<&[u8], IOReturn> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut ptr: *mut IOUSBConfigurationDescriptor = ptr::null_mut();
|
let mut ptr: *mut IOUSBConfigurationDescriptor = ptr::null_mut();
|
||||||
check_iokit_return(call_iokit_function!(
|
check_iokit_return(call_iokit_function!(
|
||||||
|
|
@ -168,7 +188,7 @@ impl IoKitDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_configuration(&self) -> Result<u8, Error> {
|
pub(crate) fn get_configuration(&self) -> Result<u8, IOReturn> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut val = 0;
|
let mut val = 0;
|
||||||
check_iokit_return(call_iokit_function!(self.raw, GetConfiguration(&mut val)))?;
|
check_iokit_return(call_iokit_function!(self.raw, GetConfiguration(&mut val)))?;
|
||||||
|
|
@ -208,12 +228,19 @@ impl IoKitInterface {
|
||||||
);
|
);
|
||||||
|
|
||||||
if rc != kIOReturnSuccess {
|
if rc != kIOReturnSuccess {
|
||||||
return Err(Error::from_raw_os_error(rc));
|
return Err(Error::new_os(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"failed to open interface",
|
||||||
|
rc,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if raw_interface_plugin.is_null() {
|
if raw_interface_plugin.is_null() {
|
||||||
error!("IOKit indicated it successfully created a PlugInInterface, but the pointer was NULL");
|
error!("IOKit indicated it successfully created a PlugInInterface, but the pointer was NULL");
|
||||||
return Err(Error::other("Could not create PlugInInterface"));
|
return Err(Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"could not create PlugInInterface",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let interface_plugin = PluginInterface::new(raw_interface_plugin);
|
let interface_plugin = PluginInterface::new(raw_interface_plugin);
|
||||||
|
|
@ -238,15 +265,15 @@ impl IoKitInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn open(&mut self) -> Result<(), Error> {
|
pub(crate) fn open(&mut self) -> Result<(), IOReturn> {
|
||||||
unsafe { check_iokit_return(call_iokit_function!(self.raw, USBInterfaceOpen())) }
|
unsafe { check_iokit_return(call_iokit_function!(self.raw, USBInterfaceOpen())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn close(&mut self) -> Result<(), Error> {
|
pub(crate) fn close(&mut self) -> Result<(), IOReturn> {
|
||||||
unsafe { check_iokit_return(call_iokit_function!(self.raw, USBInterfaceClose())) }
|
unsafe { check_iokit_return(call_iokit_function!(self.raw, USBInterfaceClose())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn create_async_event_source(&self) -> Result<CFRunLoopSource, Error> {
|
pub(crate) fn create_async_event_source(&self) -> Result<CFRunLoopSource, IOReturn> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut raw_source: CFRunLoopSourceRef = std::ptr::null_mut();
|
let mut raw_source: CFRunLoopSourceRef = std::ptr::null_mut();
|
||||||
check_iokit_return(call_iokit_function!(
|
check_iokit_return(call_iokit_function!(
|
||||||
|
|
@ -291,6 +318,24 @@ impl IoKitInterface {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_alternate_interface(&self, alt_setting: u8) -> Result<(), IOReturn> {
|
||||||
|
unsafe {
|
||||||
|
check_iokit_return(call_iokit_function!(
|
||||||
|
self.raw,
|
||||||
|
SetAlternateInterface(alt_setting)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clear_pipe_stall_both_ends(&self, pipe_ref: u8) -> Result<(), IOReturn> {
|
||||||
|
unsafe {
|
||||||
|
check_iokit_return(call_iokit_function!(
|
||||||
|
self.raw,
|
||||||
|
ClearPipeStallBothEnds(pipe_ref)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for IoKitInterface {
|
impl Drop for IoKitInterface {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
use crate::transfer::TransferError;
|
use crate::transfer::TransferError;
|
||||||
|
use crate::ErrorKind;
|
||||||
|
|
||||||
mod transfer;
|
mod transfer;
|
||||||
use io_kit_sys::ret::IOReturn;
|
use io_kit_sys::ret::IOReturn;
|
||||||
|
|
@ -37,3 +40,17 @@ fn status_to_transfer_result(status: IOReturn) -> Result<(), TransferError> {
|
||||||
_ => Err(TransferError::Unknown(status as u32)),
|
_ => Err(TransferError::Unknown(status as u32)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format_os_error_code(f: &mut std::fmt::Formatter<'_>, code: u32) -> std::fmt::Result {
|
||||||
|
write!(f, "error 0x{:08x}", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::error::Error {
|
||||||
|
pub(crate) fn new_os(kind: ErrorKind, message: &'static str, code: IOReturn) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
code: NonZeroU32::new(code as u32),
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::{btree_map::Entry, BTreeMap, VecDeque},
|
collections::{btree_map::Entry, BTreeMap, VecDeque},
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
io::{self, ErrorKind},
|
io,
|
||||||
mem::{size_of_val, transmute},
|
mem::{size_of_val, transmute},
|
||||||
os::windows::{
|
os::windows::{
|
||||||
io::{AsRawHandle, RawHandle},
|
io::{AsRawHandle, RawHandle},
|
||||||
|
|
@ -23,7 +23,8 @@ use windows_sys::Win32::{
|
||||||
},
|
},
|
||||||
Foundation::{
|
Foundation::{
|
||||||
GetLastError, ERROR_BAD_COMMAND, ERROR_DEVICE_NOT_CONNECTED, ERROR_FILE_NOT_FOUND,
|
GetLastError, ERROR_BAD_COMMAND, ERROR_DEVICE_NOT_CONNECTED, ERROR_FILE_NOT_FOUND,
|
||||||
ERROR_IO_PENDING, ERROR_NOT_FOUND, ERROR_NO_SUCH_DEVICE, FALSE, HANDLE, TRUE,
|
ERROR_IO_PENDING, ERROR_NOT_FOUND, ERROR_NO_MORE_ITEMS, ERROR_NO_SUCH_DEVICE, FALSE,
|
||||||
|
HANDLE, TRUE,
|
||||||
},
|
},
|
||||||
System::IO::{CancelIoEx, OVERLAPPED},
|
System::IO::{CancelIoEx, OVERLAPPED},
|
||||||
};
|
};
|
||||||
|
|
@ -34,7 +35,6 @@ use crate::{
|
||||||
ConfigurationDescriptor, DeviceDescriptor, EndpointDescriptor, DESCRIPTOR_LEN_DEVICE,
|
ConfigurationDescriptor, DeviceDescriptor, EndpointDescriptor, DESCRIPTOR_LEN_DEVICE,
|
||||||
DESCRIPTOR_TYPE_CONFIGURATION,
|
DESCRIPTOR_TYPE_CONFIGURATION,
|
||||||
},
|
},
|
||||||
device::ClaimEndpointError,
|
|
||||||
maybe_future::{blocking::Blocking, Ready},
|
maybe_future::{blocking::Blocking, Ready},
|
||||||
transfer::{
|
transfer::{
|
||||||
internal::{
|
internal::{
|
||||||
|
|
@ -42,7 +42,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
Buffer, Completion, ControlIn, ControlOut, Direction, Recipient, TransferError,
|
Buffer, Completion, ControlIn, ControlOut, Direction, Recipient, TransferError,
|
||||||
},
|
},
|
||||||
DeviceInfo, Error, MaybeFuture, Speed,
|
DeviceInfo, Error, ErrorKind, MaybeFuture, Speed,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
|
@ -87,7 +87,7 @@ impl WindowsDevice {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let device_descriptor = DeviceDescriptor::new(device_descriptor)
|
let device_descriptor = DeviceDescriptor::new(device_descriptor)
|
||||||
.ok_or_else(|| Error::new(ErrorKind::InvalidData, "invalid device descriptor"))?;
|
.ok_or_else(|| Error::new(ErrorKind::Other, "invalid device descriptor"))?;
|
||||||
|
|
||||||
let num_configurations = connection_info.device_desc.bNumConfigurations;
|
let num_configurations = connection_info.device_desc.bNumConfigurations;
|
||||||
let config_descriptors = (0..num_configurations)
|
let config_descriptors = (0..num_configurations)
|
||||||
|
|
@ -136,7 +136,7 @@ impl WindowsDevice {
|
||||||
&self,
|
&self,
|
||||||
_configuration: u8,
|
_configuration: u8,
|
||||||
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||||
Ready(Err(io::Error::new(
|
Ready(Err(Error::new(
|
||||||
ErrorKind::Unsupported,
|
ErrorKind::Unsupported,
|
||||||
"set_configuration not supported by WinUSB",
|
"set_configuration not supported by WinUSB",
|
||||||
)))
|
)))
|
||||||
|
|
@ -147,18 +147,24 @@ impl WindowsDevice {
|
||||||
desc_type: u8,
|
desc_type: u8,
|
||||||
desc_index: u8,
|
desc_index: u8,
|
||||||
language_id: u16,
|
language_id: u16,
|
||||||
) -> impl MaybeFuture<Output = Result<Vec<u8>, Error>> {
|
) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
|
||||||
Blocking::new(move || {
|
Blocking::new(move || {
|
||||||
HubPort::by_child_devinst(self.devinst)?.get_descriptor(
|
fn to_transfer_error(e: Error) -> TransferError {
|
||||||
desc_type,
|
match e.kind() {
|
||||||
desc_index,
|
ErrorKind::Disconnected => TransferError::Disconnected,
|
||||||
language_id,
|
_ => TransferError::Unknown(e.os_error().unwrap_or(0)),
|
||||||
)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HubPort::by_child_devinst(self.devinst)
|
||||||
|
.map_err(to_transfer_error)?
|
||||||
|
.get_descriptor(desc_type, desc_index, language_id)
|
||||||
|
.map_err(to_transfer_error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn reset(&self) -> impl MaybeFuture<Output = Result<(), Error>> {
|
pub(crate) fn reset(&self) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||||
Ready(Err(io::Error::new(
|
Ready(Err(Error::new(
|
||||||
ErrorKind::Unsupported,
|
ErrorKind::Unsupported,
|
||||||
"reset not supported by WinUSB",
|
"reset not supported by WinUSB",
|
||||||
)))
|
)))
|
||||||
|
|
@ -204,9 +210,10 @@ impl WindowsDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
debug!("Device driver is {driver:?}, not WinUSB or USBCCGP");
|
||||||
Err(Error::new(
|
Err(Error::new(
|
||||||
ErrorKind::Unsupported,
|
ErrorKind::Unsupported,
|
||||||
format!("Device driver is {driver:?}, not WinUSB or USBCCGP"),
|
"incompatible driver is installed for this device",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -266,14 +273,19 @@ unsafe impl Sync for WinusbFileHandle {}
|
||||||
|
|
||||||
impl WinusbFileHandle {
|
impl WinusbFileHandle {
|
||||||
fn new(path: &WCStr, first_interface: u8) -> Result<Self, Error> {
|
fn new(path: &WCStr, first_interface: u8) -> Result<Self, Error> {
|
||||||
let handle = create_file(path)?;
|
let handle = create_file(path)
|
||||||
|
.map_err(|e| Error::new_os(ErrorKind::Other, "failed to open device", e).log_debug())?;
|
||||||
super::events::register(&handle)?;
|
super::events::register(&handle)?;
|
||||||
|
|
||||||
let winusb_handle = unsafe {
|
let winusb_handle = unsafe {
|
||||||
let mut h = ptr::null_mut();
|
let mut h = ptr::null_mut();
|
||||||
if WinUsb_Initialize(raw_handle(&handle), &mut h) == FALSE {
|
if WinUsb_Initialize(raw_handle(&handle), &mut h) == FALSE {
|
||||||
error!("WinUsb_Initialize failed: {:?}", io::Error::last_os_error());
|
return Err(Error::new_os(
|
||||||
return Err(io::Error::last_os_error());
|
ErrorKind::Other,
|
||||||
|
"failed to initialize WinUSB",
|
||||||
|
GetLastError(),
|
||||||
|
)
|
||||||
|
.log_debug());
|
||||||
}
|
}
|
||||||
h
|
h
|
||||||
};
|
};
|
||||||
|
|
@ -296,10 +308,7 @@ impl WinusbFileHandle {
|
||||||
assert!(interface_number >= self.first_interface);
|
assert!(interface_number >= self.first_interface);
|
||||||
|
|
||||||
if self.claimed_interfaces.is_set(interface_number) {
|
if self.claimed_interfaces.is_set(interface_number) {
|
||||||
return Err(Error::new(
|
Error::new(ErrorKind::Busy, "interface is already claimed");
|
||||||
ErrorKind::AddrInUse,
|
|
||||||
"Interface is already claimed",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let winusb_handle = if self.first_interface == interface_number {
|
let winusb_handle = if self.first_interface == interface_number {
|
||||||
|
|
@ -310,13 +319,22 @@ impl WinusbFileHandle {
|
||||||
let idx = interface_number - self.first_interface - 1;
|
let idx = interface_number - self.first_interface - 1;
|
||||||
if WinUsb_GetAssociatedInterface(self.winusb_handle, idx, &mut out_handle) == FALSE
|
if WinUsb_GetAssociatedInterface(self.winusb_handle, idx, &mut out_handle) == FALSE
|
||||||
{
|
{
|
||||||
|
let err = GetLastError();
|
||||||
error!(
|
error!(
|
||||||
"WinUsb_GetAssociatedInterface for {} on {} failed: {:?}",
|
"WinUsb_GetAssociatedInterface for {} on {} failed: {:?}",
|
||||||
interface_number,
|
interface_number, self.first_interface, err
|
||||||
self.first_interface,
|
|
||||||
io::Error::last_os_error()
|
|
||||||
);
|
);
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
|
return Err(match err {
|
||||||
|
ERROR_NO_MORE_ITEMS => {
|
||||||
|
Error::new_os(ErrorKind::NotFound, "interface not found", err)
|
||||||
|
}
|
||||||
|
_ => Error::new_os(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"failed to initialize WinUSB for associated interface",
|
||||||
|
err,
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
out_handle
|
out_handle
|
||||||
}
|
}
|
||||||
|
|
@ -463,9 +481,9 @@ impl WindowsInterface {
|
||||||
Blocking::new(move || unsafe {
|
Blocking::new(move || unsafe {
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
if !state.endpoints.is_empty() {
|
if !state.endpoints.is_empty() {
|
||||||
// TODO: Use ErrorKind::ResourceBusy once compatible with MSRV
|
return Err(Error::new(
|
||||||
return Err(Error::other(
|
ErrorKind::Busy,
|
||||||
"must drop endpoints before changing alt setting",
|
"can't change alternate setting while endpoints are in use",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let r = WinUsb_SetCurrentAlternateSetting(self.winusb_handle, alt_setting);
|
let r = WinUsb_SetCurrentAlternateSetting(self.winusb_handle, alt_setting);
|
||||||
|
|
@ -477,7 +495,15 @@ impl WindowsInterface {
|
||||||
state.alt_setting = alt_setting;
|
state.alt_setting = alt_setting;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(io::Error::last_os_error())
|
Err(match GetLastError() {
|
||||||
|
e @ ERROR_NOT_FOUND => {
|
||||||
|
Error::new_os(ErrorKind::NotFound, "alternate setting not found", e)
|
||||||
|
}
|
||||||
|
e @ ERROR_BAD_COMMAND => {
|
||||||
|
Error::new_os(ErrorKind::Disconnected, "device disconnected", e)
|
||||||
|
}
|
||||||
|
e => Error::new_os(ErrorKind::Other, "failed to set alternate setting", e),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -489,14 +515,14 @@ impl WindowsInterface {
|
||||||
pub fn endpoint(
|
pub fn endpoint(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
descriptor: EndpointDescriptor,
|
descriptor: EndpointDescriptor,
|
||||||
) -> Result<WindowsEndpoint, ClaimEndpointError> {
|
) -> Result<WindowsEndpoint, Error> {
|
||||||
let address = descriptor.address();
|
let address = descriptor.address();
|
||||||
let max_packet_size = descriptor.max_packet_size();
|
let max_packet_size = descriptor.max_packet_size();
|
||||||
|
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
if state.endpoints.is_set(address) {
|
if state.endpoints.is_set(address) {
|
||||||
return Err(ClaimEndpointError::Busy);
|
return Err(Error::new(ErrorKind::Busy, "endpoint already in use"));
|
||||||
}
|
}
|
||||||
state.endpoints.set(address);
|
state.endpoints.set(address);
|
||||||
|
|
||||||
|
|
@ -730,11 +756,12 @@ impl WindowsEndpoint {
|
||||||
let endpoint = inner.address;
|
let endpoint = inner.address;
|
||||||
debug!("Clear halt, endpoint {endpoint:02x}");
|
debug!("Clear halt, endpoint {endpoint:02x}");
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = WinUsb_ResetPipe(inner.interface.winusb_handle, endpoint);
|
if WinUsb_ResetPipe(inner.interface.winusb_handle, endpoint) == TRUE {
|
||||||
if r == TRUE {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(io::Error::last_os_error())
|
Err(match GetLastError() {
|
||||||
|
e => Error::new_os(ErrorKind::Other, "failed to clear halt", e),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
use std::{
|
use std::ffi::{OsStr, OsString};
|
||||||
ffi::{OsStr, OsString},
|
|
||||||
io::ErrorKind,
|
|
||||||
};
|
|
||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use windows_sys::Win32::Devices::{
|
use windows_sys::Win32::Devices::{
|
||||||
|
|
@ -19,7 +16,7 @@ use crate::{
|
||||||
DESCRIPTOR_TYPE_CONFIGURATION, DESCRIPTOR_TYPE_STRING,
|
DESCRIPTOR_TYPE_CONFIGURATION, DESCRIPTOR_TYPE_STRING,
|
||||||
},
|
},
|
||||||
maybe_future::{blocking::Blocking, MaybeFuture},
|
maybe_future::{blocking::Blocking, MaybeFuture},
|
||||||
BusInfo, DeviceInfo, Error, InterfaceInfo, UsbControllerType,
|
BusInfo, DeviceInfo, Error, ErrorKind, InterfaceInfo, UsbControllerType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
|
@ -241,7 +238,10 @@ pub(crate) fn get_winusb_device_path(dev: DevInst) -> Result<WCString, Error> {
|
||||||
let paths = dev.interfaces(GUID_DEVINTERFACE_USB_DEVICE);
|
let paths = dev.interfaces(GUID_DEVINTERFACE_USB_DEVICE);
|
||||||
|
|
||||||
let Some(path) = paths.iter().next() else {
|
let Some(path) = paths.iter().next() else {
|
||||||
return Err(Error::other("Failed to find device path for WinUSB device"));
|
return Err(Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"failed to find device path for WinUSB device",
|
||||||
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(path.to_owned())
|
Ok(path.to_owned())
|
||||||
|
|
@ -266,14 +266,15 @@ pub(crate) fn get_usbccgp_winusb_device_path(child: DevInst) -> Result<WCString,
|
||||||
let Some(driver) = child.get_property::<OsString>(DEVPKEY_Device_Service) else {
|
let Some(driver) = child.get_property::<OsString>(DEVPKEY_Device_Service) else {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
ErrorKind::Unsupported,
|
ErrorKind::Unsupported,
|
||||||
"Could not determine driver for interface",
|
"could not determine driver for interface",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
if !driver.eq_ignore_ascii_case("winusb") {
|
if !driver.eq_ignore_ascii_case("winusb") {
|
||||||
|
debug!("Incompatible driver {driver:?} for interface, not WinUSB");
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
ErrorKind::Unsupported,
|
ErrorKind::Unsupported,
|
||||||
format!("Interface driver is {driver:?}, not WinUSB"),
|
"incompatible driver is installed for this interface",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,9 +299,11 @@ pub(crate) fn get_usbccgp_winusb_device_path(child: DevInst) -> Result<WCString,
|
||||||
|
|
||||||
let paths = child.interfaces(guid);
|
let paths = child.interfaces(guid);
|
||||||
let Some(path) = paths.iter().next() else {
|
let Some(path) = paths.iter().next() else {
|
||||||
return Err(Error::other(
|
return Err(Error::new(
|
||||||
"Failed to find device path for WinUSB interface",
|
ErrorKind::Unsupported,
|
||||||
));
|
"failed to find device path for WinUSB interface",
|
||||||
|
)
|
||||||
|
.log_debug());
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(path.to_owned())
|
Ok(path.to_owned())
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use log::error;
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use std::{
|
use std::{
|
||||||
os::windows::{
|
os::windows::{
|
||||||
|
|
@ -24,11 +23,12 @@ impl IoCompletionPort {
|
||||||
let port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, ptr::null_mut(), 0, 0);
|
let port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, ptr::null_mut(), 0, 0);
|
||||||
match HandleOrNull::from_raw_handle(port as RawHandle).try_into() {
|
match HandleOrNull::from_raw_handle(port as RawHandle).try_into() {
|
||||||
Ok(handle) => Ok(IoCompletionPort(handle)),
|
Ok(handle) => Ok(IoCompletionPort(handle)),
|
||||||
Err(_) => {
|
Err(_) => Err(Error::new_os(
|
||||||
let err = GetLastError();
|
crate::ErrorKind::Other,
|
||||||
error!("CreateIoCompletionPort (create) failed: {err:?}");
|
"failed to create IO completion port",
|
||||||
Err(std::io::Error::from_raw_os_error(err as i32))
|
GetLastError(),
|
||||||
}
|
)
|
||||||
|
.log_error()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,9 +37,12 @@ impl IoCompletionPort {
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = CreateIoCompletionPort(raw_handle(handle), raw_handle(&self.0), 0, 0);
|
let r = CreateIoCompletionPort(raw_handle(handle), raw_handle(&self.0), 0, 0);
|
||||||
if r.is_null() {
|
if r.is_null() {
|
||||||
let err = std::io::Error::last_os_error();
|
Err(Error::new_os(
|
||||||
error!("CreateIoCompletionPort (register) failed: {err:?}");
|
crate::ErrorKind::Other,
|
||||||
Err(err)
|
"failed to register IO completion port",
|
||||||
|
GetLastError(),
|
||||||
|
)
|
||||||
|
.log_error())
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -62,9 +65,12 @@ impl IoCompletionPort {
|
||||||
);
|
);
|
||||||
|
|
||||||
if r == FALSE {
|
if r == FALSE {
|
||||||
let err = std::io::Error::last_os_error();
|
Err(Error::new_os(
|
||||||
error!("GetQueuedCompletionStatusEx failed: {err:?}");
|
crate::ErrorKind::Other,
|
||||||
Err(err)
|
"failed to get events from IO completion port",
|
||||||
|
GetLastError(),
|
||||||
|
)
|
||||||
|
.log_error())
|
||||||
} else {
|
} else {
|
||||||
events.set_len(event_count as usize);
|
events.set_len(event_count as usize);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
task::{Context, Poll, Waker},
|
task::{Context, Poll, Waker},
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::{debug, error};
|
use log::debug;
|
||||||
use windows_sys::Win32::{
|
use windows_sys::Win32::{
|
||||||
Devices::{
|
Devices::{
|
||||||
DeviceAndDriverInstallation::{
|
DeviceAndDriverInstallation::{
|
||||||
|
|
@ -78,8 +78,12 @@ impl WindowsHotplugWatch {
|
||||||
};
|
};
|
||||||
|
|
||||||
if cr != CR_SUCCESS {
|
if cr != CR_SUCCESS {
|
||||||
error!("CM_Register_Notification failed: {cr}");
|
return Err(Error::new_os(
|
||||||
return Err(Error::other("Failed to initialize hotplug notifications"));
|
crate::ErrorKind::Other,
|
||||||
|
"failed to initialize hotplug notifications",
|
||||||
|
cr,
|
||||||
|
)
|
||||||
|
.log_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(WindowsHotplugWatch {
|
Ok(WindowsHotplugWatch {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
alloc::{self, Layout},
|
alloc::{self, Layout},
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
io::ErrorKind,
|
|
||||||
mem,
|
mem,
|
||||||
os::windows::prelude::OwnedHandle,
|
os::windows::prelude::OwnedHandle,
|
||||||
ptr::{addr_of, null_mut},
|
ptr::{addr_of, null_mut},
|
||||||
|
|
@ -25,6 +24,13 @@ use windows_sys::Win32::{
|
||||||
System::IO::DeviceIoControl,
|
System::IO::DeviceIoControl,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::{descriptors::DESCRIPTOR_TYPE_DEVICE, Error, ErrorKind, Speed};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
cfgmgr32::DevInst,
|
||||||
|
util::{create_file, raw_handle},
|
||||||
|
};
|
||||||
|
|
||||||
// flags for USB_NODE_CONNECTION_INFORMATION_EX_V2.SupportedUsbProtocols
|
// flags for USB_NODE_CONNECTION_INFORMATION_EX_V2.SupportedUsbProtocols
|
||||||
const USB110: u32 = 0x01;
|
const USB110: u32 = 0x01;
|
||||||
const USB200: u32 = 0x02;
|
const USB200: u32 = 0x02;
|
||||||
|
|
@ -36,13 +42,6 @@ const DEVICE_IS_SUPER_SPEED_CAPABLE_OR_HIGHER: u32 = 0x02;
|
||||||
const DEVICE_IS_OPERATING_AT_SUPER_SPEED_PLUS_OR_HIGHER: u32 = 0x04;
|
const DEVICE_IS_OPERATING_AT_SUPER_SPEED_PLUS_OR_HIGHER: u32 = 0x04;
|
||||||
const DEVICE_IS_SUPER_SPEED_PLUS_CAPABLE_OR_HIGHER: u32 = 0x08;
|
const DEVICE_IS_SUPER_SPEED_PLUS_CAPABLE_OR_HIGHER: u32 = 0x08;
|
||||||
|
|
||||||
use crate::{descriptors::DESCRIPTOR_TYPE_DEVICE, Error, Speed};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
cfgmgr32::DevInst,
|
|
||||||
util::{create_file, raw_handle},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Safe wrapper around hub ioctls used to get descriptors for child devices.
|
/// Safe wrapper around hub ioctls used to get descriptors for child devices.
|
||||||
pub struct HubHandle(OwnedHandle);
|
pub struct HubHandle(OwnedHandle);
|
||||||
|
|
||||||
|
|
@ -83,19 +82,25 @@ impl HubHandle {
|
||||||
);
|
);
|
||||||
|
|
||||||
if r == TRUE {
|
if r == TRUE {
|
||||||
|
if info.ConnectionStatus != windows_sys::Win32::Devices::Usb::DeviceConnected {
|
||||||
|
return Err(Error::new(ErrorKind::Disconnected, "device disconnected"));
|
||||||
|
}
|
||||||
if info.DeviceDescriptor.bDescriptorType != DESCRIPTOR_TYPE_DEVICE {
|
if info.DeviceDescriptor.bDescriptorType != DESCRIPTOR_TYPE_DEVICE {
|
||||||
// When the device is disconnected during this call, Windows is observed to
|
// When the device is disconnected during this call, Windows is observed to
|
||||||
// sometimes return an all-zero device descriptor.
|
// sometimes return an all-zero device descriptor.
|
||||||
return Err(Error::other(
|
return Err(Error::new(ErrorKind::Other,
|
||||||
"IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX returned an invalid device descriptor",
|
"IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX returned an invalid device descriptor",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(info)
|
Ok(info)
|
||||||
} else {
|
} else {
|
||||||
let err = Error::last_os_error();
|
Err(Error::new_os(
|
||||||
debug!("Hub DeviceIoControl failed: {err:?}");
|
ErrorKind::Other,
|
||||||
Err(err)
|
"hub DeviceIoControl failed",
|
||||||
|
GetLastError(),
|
||||||
|
)
|
||||||
|
.log_debug())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -124,9 +129,12 @@ impl HubHandle {
|
||||||
if r == TRUE {
|
if r == TRUE {
|
||||||
Ok(info)
|
Ok(info)
|
||||||
} else {
|
} else {
|
||||||
let err = Error::last_os_error();
|
Err(Error::new_os(
|
||||||
debug!("Hub DeviceIoControl failed: {err:?}");
|
ErrorKind::Other,
|
||||||
Err(err)
|
"hub DeviceIoControl failed",
|
||||||
|
GetLastError(),
|
||||||
|
)
|
||||||
|
.log_debug())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -186,10 +194,12 @@ impl HubHandle {
|
||||||
let err = GetLastError();
|
let err = GetLastError();
|
||||||
debug!("IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION failed: type={descriptor_type} index={descriptor_index} error={err:?}");
|
debug!("IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION failed: type={descriptor_type} index={descriptor_index} error={err:?}");
|
||||||
Err(match err {
|
Err(match err {
|
||||||
ERROR_GEN_FAILURE => {
|
ERROR_GEN_FAILURE => Error::new_os(
|
||||||
Error::other("Descriptor request failed. Device might be suspended.")
|
ErrorKind::Other,
|
||||||
}
|
"descriptor request failed: device might be suspended.",
|
||||||
_ => Error::from_raw_os_error(err as i32),
|
err,
|
||||||
|
),
|
||||||
|
_ => Error::new_os(ErrorKind::Other, "descriptor request failed", err),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -215,12 +225,12 @@ impl HubPort {
|
||||||
pub fn by_child_devinst(devinst: DevInst) -> Result<HubPort, Error> {
|
pub fn by_child_devinst(devinst: DevInst) -> Result<HubPort, Error> {
|
||||||
let parent_hub = devinst
|
let parent_hub = devinst
|
||||||
.parent()
|
.parent()
|
||||||
.ok_or_else(|| Error::other("failed to find parent hub"))?;
|
.ok_or_else(|| Error::new(ErrorKind::Disconnected, "failed to find parent hub"))?;
|
||||||
let hub_handle = HubHandle::by_devinst(parent_hub)
|
let hub_handle = HubHandle::by_devinst(parent_hub)
|
||||||
.ok_or_else(|| Error::other("failed to open parent hub"))?;
|
.ok_or_else(|| Error::new(ErrorKind::Disconnected, "failed to open parent hub"))?;
|
||||||
let Some(port_number) = devinst.get_property::<u32>(DEVPKEY_Device_Address) else {
|
let Some(port_number) = devinst.get_property::<u32>(DEVPKEY_Device_Address) else {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
ErrorKind::NotConnected,
|
ErrorKind::Disconnected,
|
||||||
"Could not find hub port number",
|
"Could not find hub port number",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
mod enumeration;
|
mod enumeration;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
pub use enumeration::{list_buses, list_devices};
|
pub use enumeration::{list_buses, list_devices};
|
||||||
|
|
||||||
mod events;
|
mod events;
|
||||||
|
|
@ -14,7 +16,24 @@ mod cfgmgr32;
|
||||||
mod hub;
|
mod hub;
|
||||||
mod registry;
|
mod registry;
|
||||||
pub(crate) use cfgmgr32::DevInst;
|
pub(crate) use cfgmgr32::DevInst;
|
||||||
|
use windows_sys::Win32::Foundation::WIN32_ERROR;
|
||||||
pub(crate) use DevInst as DeviceId;
|
pub(crate) use DevInst as DeviceId;
|
||||||
mod hotplug;
|
mod hotplug;
|
||||||
mod util;
|
mod util;
|
||||||
pub(crate) use hotplug::WindowsHotplugWatch as HotplugWatch;
|
pub(crate) use hotplug::WindowsHotplugWatch as HotplugWatch;
|
||||||
|
|
||||||
|
use crate::ErrorKind;
|
||||||
|
|
||||||
|
pub fn format_os_error_code(f: &mut std::fmt::Formatter<'_>, code: u32) -> std::fmt::Result {
|
||||||
|
write!(f, "error {}", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::error::Error {
|
||||||
|
pub(crate) fn new_os(kind: ErrorKind, message: &'static str, code: WIN32_ERROR) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
code: NonZeroU32::new(code as u32),
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
alloc::{self, Layout},
|
alloc::{self, Layout},
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
io::ErrorKind,
|
|
||||||
mem,
|
mem,
|
||||||
ptr::{null, null_mut},
|
ptr::{null, null_mut},
|
||||||
};
|
};
|
||||||
|
|
@ -9,7 +8,7 @@ use std::{
|
||||||
use windows_sys::{
|
use windows_sys::{
|
||||||
core::GUID,
|
core::GUID,
|
||||||
Win32::{
|
Win32::{
|
||||||
Foundation::{ERROR_SUCCESS, S_OK},
|
Foundation::{GetLastError, ERROR_SUCCESS, S_OK},
|
||||||
System::{
|
System::{
|
||||||
Com::IIDFromString,
|
Com::IIDFromString,
|
||||||
Registry::{RegCloseKey, RegQueryValueExW, HKEY, REG_MULTI_SZ, REG_SZ},
|
Registry::{RegCloseKey, RegQueryValueExW, HKEY, REG_MULTI_SZ, REG_SZ},
|
||||||
|
|
@ -45,13 +44,18 @@ impl RegKey {
|
||||||
);
|
);
|
||||||
|
|
||||||
if r != ERROR_SUCCESS {
|
if r != ERROR_SUCCESS {
|
||||||
return Err(Error::from_raw_os_error(r as i32));
|
return Err(Error::new_os(
|
||||||
|
crate::ErrorKind::Other,
|
||||||
|
"failed to read registry value",
|
||||||
|
GetLastError(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ty != REG_MULTI_SZ && ty != REG_SZ {
|
if ty != REG_MULTI_SZ && ty != REG_SZ {
|
||||||
return Err(Error::new(
|
return Err(Error::new_os(
|
||||||
ErrorKind::InvalidInput,
|
crate::ErrorKind::Other,
|
||||||
"registry value type not string",
|
"failed to read registry value: expected string",
|
||||||
|
GetLastError(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +67,11 @@ impl RegKey {
|
||||||
|
|
||||||
if r != ERROR_SUCCESS {
|
if r != ERROR_SUCCESS {
|
||||||
alloc::dealloc(buf, layout);
|
alloc::dealloc(buf, layout);
|
||||||
return Err(Error::from_raw_os_error(r as i32));
|
return Err(Error::new_os(
|
||||||
|
crate::ErrorKind::Other,
|
||||||
|
"failed to read registry value data",
|
||||||
|
GetLastError(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut guid = GUID::from_u128(0);
|
let mut guid = GUID::from_u128(0);
|
||||||
|
|
@ -74,7 +82,10 @@ impl RegKey {
|
||||||
if r == S_OK {
|
if r == S_OK {
|
||||||
Ok(guid)
|
Ok(guid)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::new(ErrorKind::InvalidData, "invalid UUID"))
|
Err(Error::new(
|
||||||
|
crate::ErrorKind::Other,
|
||||||
|
"failed to parse GUID from registry value",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ use std::{
|
||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
ffi::{OsStr, OsString},
|
ffi::{OsStr, OsString},
|
||||||
fmt::{Display, Write},
|
fmt::{Display, Write},
|
||||||
io,
|
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
os::windows::prelude::{
|
os::windows::prelude::{
|
||||||
AsHandle, AsRawHandle, HandleOrInvalid, OsStrExt, OsStringExt, OwnedHandle, RawHandle,
|
AsHandle, AsRawHandle, HandleOrInvalid, OsStrExt, OsStringExt, OwnedHandle, RawHandle,
|
||||||
|
|
@ -12,14 +11,14 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use windows_sys::Win32::{
|
use windows_sys::Win32::{
|
||||||
Foundation::{GENERIC_READ, GENERIC_WRITE, HANDLE},
|
Foundation::{GetLastError, GENERIC_READ, GENERIC_WRITE, HANDLE, WIN32_ERROR},
|
||||||
Storage::FileSystem::{
|
Storage::FileSystem::{
|
||||||
CreateFileW, FILE_FLAG_OVERLAPPED, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
|
CreateFileW, FILE_FLAG_OVERLAPPED, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Wrapper around `CreateFile`
|
/// Wrapper around `CreateFile`
|
||||||
pub fn create_file(path: &WCStr) -> Result<OwnedHandle, io::Error> {
|
pub fn create_file(path: &WCStr) -> Result<OwnedHandle, WIN32_ERROR> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = CreateFileW(
|
let r = CreateFileW(
|
||||||
path.as_ptr(),
|
path.as_ptr(),
|
||||||
|
|
@ -32,7 +31,7 @@ pub fn create_file(path: &WCStr) -> Result<OwnedHandle, io::Error> {
|
||||||
);
|
);
|
||||||
HandleOrInvalid::from_raw_handle(r as RawHandle)
|
HandleOrInvalid::from_raw_handle(r as RawHandle)
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| io::Error::last_os_error())
|
.map_err(|_| GetLastError())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ pub use buffer::Buffer;
|
||||||
|
|
||||||
pub(crate) mod internal;
|
pub(crate) mod internal;
|
||||||
|
|
||||||
use crate::descriptors::TransferType;
|
use crate::{descriptors::TransferType, platform};
|
||||||
|
|
||||||
/// Transfer error.
|
/// Transfer error.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -60,11 +60,9 @@ impl Display for TransferError {
|
||||||
TransferError::Fault => write!(f, "hardware fault or protocol violation"),
|
TransferError::Fault => write!(f, "hardware fault or protocol violation"),
|
||||||
TransferError::InvalidArgument => write!(f, "invalid or unsupported argument"),
|
TransferError::InvalidArgument => write!(f, "invalid or unsupported argument"),
|
||||||
TransferError::Unknown(e) => {
|
TransferError::Unknown(e) => {
|
||||||
if cfg!(target_os = "macos") {
|
write!(f, "unknown (")?;
|
||||||
write!(f, "unknown error (0x{e:08x})")
|
platform::format_os_error_code(f, *e)?;
|
||||||
} else {
|
write!(f, ")")
|
||||||
write!(f, "unknown error ({e})")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue