Merge pull request #102 from kirisauce/dev
Add support for getting device descriptor and speed from opened device
This commit is contained in:
commit
a34c4497b7
10 changed files with 334 additions and 29 deletions
|
|
@ -25,6 +25,10 @@ fn inspect_device(dev: DeviceInfo) {
|
|||
}
|
||||
};
|
||||
|
||||
println!("{:#?}", dev.device_descriptor());
|
||||
|
||||
println!("Speed: {:?}", dev.speed());
|
||||
|
||||
match dev.active_configuration() {
|
||||
Ok(config) => println!("Active configuration is {}", config.configuration_value()),
|
||||
Err(e) => println!("Unknown active configuration: {e}"),
|
||||
|
|
|
|||
|
|
@ -29,14 +29,7 @@ fn inspect_device(dev: DeviceInfo) {
|
|||
|
||||
let timeout = Duration::from_millis(100);
|
||||
|
||||
let dev_descriptor = dev.get_descriptor(0x01, 0, 0, timeout).unwrap();
|
||||
if dev_descriptor.len() < 18
|
||||
|| dev_descriptor[0] as usize > dev_descriptor.len()
|
||||
|| dev_descriptor[1] != 0x01
|
||||
{
|
||||
println!(" Invalid device descriptor: {dev_descriptor:?}");
|
||||
return;
|
||||
}
|
||||
let dev_descriptor = dev.device_descriptor();
|
||||
|
||||
let languages: Vec<u16> = dev
|
||||
.get_string_descriptor_supported_languages(timeout)
|
||||
|
|
@ -46,20 +39,17 @@ fn inspect_device(dev: DeviceInfo) {
|
|||
|
||||
let language = languages.first().copied().unwrap_or(US_ENGLISH);
|
||||
|
||||
let i_manufacturer = dev_descriptor[14];
|
||||
if i_manufacturer != 0 {
|
||||
if let Some(i_manufacturer) = dev_descriptor.manufacturer_string_index() {
|
||||
let s = dev.get_string_descriptor(i_manufacturer, language, timeout);
|
||||
println!(" Manufacturer({i_manufacturer}): {s:?}");
|
||||
}
|
||||
|
||||
let i_product = dev_descriptor[15];
|
||||
if i_product != 0 {
|
||||
if let Some(i_product) = dev_descriptor.product_string_index() {
|
||||
let s = dev.get_string_descriptor(i_product, language, timeout);
|
||||
println!(" Product({i_product}): {s:?}");
|
||||
}
|
||||
|
||||
let i_serial = dev_descriptor[16];
|
||||
if i_serial != 0 {
|
||||
if let Some(i_serial) = dev_descriptor.serial_number_string_index() {
|
||||
let s = dev.get_string_descriptor(i_serial, language, timeout);
|
||||
println!(" Serial({i_serial}): {s:?}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use crate::{
|
|||
Error,
|
||||
};
|
||||
|
||||
pub(crate) const DESCRIPTOR_TYPE_DEVICE: u8 = 0x01;
|
||||
pub(crate) const DESCRIPTOR_LEN_DEVICE: u8 = 18;
|
||||
|
||||
pub(crate) const DESCRIPTOR_TYPE_CONFIGURATION: u8 = 0x02;
|
||||
|
|
@ -164,13 +165,13 @@ impl<'a> Iterator for Descriptors<'a> {
|
|||
}
|
||||
|
||||
macro_rules! descriptor_fields {
|
||||
(impl<'a> $tname:ident<'a> {
|
||||
(impl $(<$( $i_lt:lifetime ),+>)? $tname:ident $(<$( $t_lt:lifetime ),+>)? {
|
||||
$(
|
||||
$(#[$attr:meta])*
|
||||
$vis:vis fn $name:ident at $pos:literal -> $ty:ty;
|
||||
)*
|
||||
}) => {
|
||||
impl<'a> $tname<'a> {
|
||||
impl $(<$( $i_lt ),+>)? $tname $(<$( $t_lt ),+>)? {
|
||||
$(
|
||||
$(#[$attr])*
|
||||
#[inline]
|
||||
|
|
@ -180,6 +181,189 @@ macro_rules! descriptor_fields {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check whether the buffer contains a valid device descriptor.
|
||||
/// On success, it will return length of the descriptor, or returns `None`.
|
||||
#[allow(unused)]
|
||||
pub(crate) fn validate_device_descriptor(buf: &[u8]) -> Option<usize> {
|
||||
if buf.len() < DESCRIPTOR_LEN_DEVICE as usize {
|
||||
if buf.len() != 0 {
|
||||
warn!(
|
||||
"device descriptor buffer is {} bytes, need {}",
|
||||
buf.len(),
|
||||
DESCRIPTOR_LEN_DEVICE
|
||||
);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
if buf[0] < DESCRIPTOR_LEN_DEVICE {
|
||||
warn!("invalid device descriptor bLength");
|
||||
return None;
|
||||
}
|
||||
|
||||
if buf[1] != DESCRIPTOR_TYPE_DEVICE {
|
||||
warn!(
|
||||
"device bDescriptorType is {}, not a device descriptor",
|
||||
buf[1]
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(buf[0] as usize);
|
||||
}
|
||||
|
||||
/// Information about a USB device.
|
||||
#[derive(Clone)]
|
||||
pub struct DeviceDescriptor([u8; DESCRIPTOR_LEN_DEVICE as usize]);
|
||||
|
||||
impl DeviceDescriptor {
|
||||
/// Create a `DeviceDescriptor` from a buffer beginning with a device descriptor.
|
||||
///
|
||||
/// You normally obtain a `DeviceDescriptor` from a [`Device`][crate::Device], but this allows creating
|
||||
/// one from your own descriptor bytes for tests.
|
||||
///
|
||||
/// ### Panics
|
||||
/// * when the buffer is too short for a device descriptor
|
||||
/// * when the first descriptor is not a device descriptor
|
||||
pub fn new(buf: &[u8]) -> Self {
|
||||
assert!(buf.len() >= DESCRIPTOR_LEN_DEVICE as usize);
|
||||
assert!(buf[0] as usize >= DESCRIPTOR_LEN_DEVICE as usize);
|
||||
assert!(buf[1] == DESCRIPTOR_TYPE_DEVICE);
|
||||
Self(buf[0..DESCRIPTOR_LEN_DEVICE as usize].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// Get the bytes of the descriptor.
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn from_fields(
|
||||
usb_version: u16,
|
||||
class: u8,
|
||||
subclass: u8,
|
||||
protocol: u8,
|
||||
max_packet_size_0: u8,
|
||||
vendor_id: u16,
|
||||
product_id: u16,
|
||||
device_version: u16,
|
||||
manufacturer_string_index: u8,
|
||||
product_string_index: u8,
|
||||
serial_number_string_index: u8,
|
||||
num_configurations: u8,
|
||||
) -> DeviceDescriptor {
|
||||
DeviceDescriptor([
|
||||
DESCRIPTOR_LEN_DEVICE,
|
||||
DESCRIPTOR_TYPE_DEVICE,
|
||||
usb_version.to_le_bytes()[0],
|
||||
usb_version.to_le_bytes()[1],
|
||||
class,
|
||||
subclass,
|
||||
protocol,
|
||||
max_packet_size_0,
|
||||
vendor_id.to_le_bytes()[0],
|
||||
vendor_id.to_le_bytes()[1],
|
||||
product_id.to_le_bytes()[0],
|
||||
product_id.to_le_bytes()[1],
|
||||
device_version.to_le_bytes()[0],
|
||||
device_version.to_le_bytes()[1],
|
||||
manufacturer_string_index,
|
||||
product_string_index,
|
||||
serial_number_string_index,
|
||||
num_configurations,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
descriptor_fields! {
|
||||
impl DeviceDescriptor {
|
||||
/// `bcdUSB` descriptor field: USB Specification Number.
|
||||
#[doc(alias = "bcdUSB")]
|
||||
pub fn usb_version at 2 -> u16;
|
||||
|
||||
/// `bDeviceClass` descriptor field: Class code, assigned by USB-IF.
|
||||
#[doc(alias = "bDeviceClass")]
|
||||
pub fn class at 4 -> u8;
|
||||
|
||||
/// `bDeviceSubClass` descriptor field: Subclass code, assigned by USB-IF.
|
||||
#[doc(alias = "bDeviceSubClass")]
|
||||
pub fn subclass at 5 -> u8;
|
||||
|
||||
/// `bDeviceProtocol` descriptor field: Protocol code, assigned by USB-IF.
|
||||
#[doc(alias = "bDeviceProtocol")]
|
||||
pub fn protocol at 6 -> u8;
|
||||
|
||||
/// `bMaxPacketSize0` descriptor field: Maximum packet size for 0 Endpoint.
|
||||
#[doc(alias = "bMaxPacketSize0")]
|
||||
pub fn max_packet_size_0 at 7 -> u8;
|
||||
|
||||
/// `idVendor` descriptor field: Vendor ID, assigned by USB-IF.
|
||||
#[doc(alias = "idVendor")]
|
||||
pub fn vendor_id at 8 -> u16;
|
||||
|
||||
/// `idProduct` descriptor field: Product ID, assigned by the manufacturer.
|
||||
#[doc(alias = "idProduct")]
|
||||
pub fn product_id at 10 -> u16;
|
||||
|
||||
/// `bcdDevice` descriptor field: Device release number.
|
||||
#[doc(alias = "bcdDevice")]
|
||||
pub fn device_version at 12 -> u16;
|
||||
|
||||
fn manufacturer_string_index_raw at 14 -> u8;
|
||||
fn product_string_index_raw at 15 -> u8;
|
||||
fn serial_number_string_index_raw at 16 -> u8;
|
||||
|
||||
/// `bNumConfigurations` descriptor field: Number of configurations
|
||||
#[doc(alias = "bNumConfigurations")]
|
||||
pub fn num_configurations at 17 -> u8;
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceDescriptor {
|
||||
/// `iManufacturer` descriptor field: Index for manufacturer description string.
|
||||
pub fn manufacturer_string_index(&self) -> Option<u8> {
|
||||
Some(self.manufacturer_string_index_raw()).filter(|&i| i != 0)
|
||||
}
|
||||
|
||||
/// `iProduct` descriptor field: Index for product description string.
|
||||
pub fn product_string_index(&self) -> Option<u8> {
|
||||
Some(self.product_string_index_raw()).filter(|&i| i != 0)
|
||||
}
|
||||
|
||||
/// `iSerialNumber` descriptor field: Index for serial number string.
|
||||
pub fn serial_number_string_index(&self) -> Option<u8> {
|
||||
Some(self.serial_number_string_index_raw()).filter(|&i| i != 0)
|
||||
}
|
||||
}
|
||||
impl Debug for DeviceDescriptor {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("DeviceDescriptor")
|
||||
.field("usb_version", &format_args!("0x{:04X}", self.usb_version()))
|
||||
.field("class", &format_args!("0x{:02X}", self.class()))
|
||||
.field("subclass", &format_args!("0x{:02X}", self.subclass()))
|
||||
.field("protocol", &format_args!("0x{:02X}", self.protocol()))
|
||||
.field("max_packet_size_0", &self.max_packet_size_0())
|
||||
.field("vendor_id", &format_args!("0x{:04X}", self.vendor_id()))
|
||||
.field("product_id", &format_args!("0x{:04X}", self.product_id()))
|
||||
.field(
|
||||
"device_version",
|
||||
&format_args!("0x{:04X}", self.device_version()),
|
||||
)
|
||||
.field(
|
||||
"manufacturer_string_index",
|
||||
&self.manufacturer_string_index(),
|
||||
)
|
||||
.field("product_string_index", &self.product_string_index())
|
||||
.field(
|
||||
"serial_number_string_index",
|
||||
&self.serial_number_string_index(),
|
||||
)
|
||||
.field("num_configurations", &self.num_configurations())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn validate_config_descriptor(buf: &[u8]) -> Option<usize> {
|
||||
if buf.len() < DESCRIPTOR_LEN_CONFIGURATION as usize {
|
||||
if buf.len() != 0 {
|
||||
|
|
@ -549,6 +733,7 @@ impl From<ActiveConfigurationError> for Error {
|
|||
}
|
||||
|
||||
/// Split a chain of concatenated configuration descriptors by `wTotalLength`
|
||||
#[allow(unused)]
|
||||
pub(crate) fn parse_concatenated_config_descriptors(mut buf: &[u8]) -> impl Iterator<Item = &[u8]> {
|
||||
iter::from_fn(move || {
|
||||
let total_len = validate_config_descriptor(buf)?;
|
||||
|
|
@ -659,6 +844,23 @@ fn test_malformed() {
|
|||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn test_linux_root_hub() {
|
||||
let dev = DeviceDescriptor::new(&[
|
||||
0x12, 0x01, 0x00, 0x02, 0x09, 0x00, 0x01, 0x40, 0x6b,
|
||||
0x1d, 0x02, 0x00, 0x10, 0x05, 0x03, 0x02, 0x01, 0x01
|
||||
]);
|
||||
assert_eq!(dev.usb_version(), 0x0200);
|
||||
assert_eq!(dev.class(), 0x09);
|
||||
assert_eq!(dev.subclass(), 0x00);
|
||||
assert_eq!(dev.protocol(), 0x01);
|
||||
assert_eq!(dev.max_packet_size_0(), 64);
|
||||
assert_eq!(dev.vendor_id(), 0x1d6b);
|
||||
assert_eq!(dev.product_id(), 0x0002);
|
||||
assert_eq!(dev.device_version(), 0x0510);
|
||||
assert_eq!(dev.manufacturer_string_index(), Some(3));
|
||||
assert_eq!(dev.product_string_index(), Some(2));
|
||||
assert_eq!(dev.serial_number_string_index(), Some(1));
|
||||
assert_eq!(dev.num_configurations(), 1);
|
||||
|
||||
let c = Configuration(&[
|
||||
0x09, 0x02, 0x19, 0x00, 0x01, 0x01, 0x00, 0xe0, 0x00,
|
||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x00,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
use crate::{
|
||||
descriptors::{
|
||||
decode_string_descriptor, validate_string_descriptor, ActiveConfigurationError,
|
||||
Configuration, InterfaceAltSetting, DESCRIPTOR_TYPE_STRING,
|
||||
Configuration, DeviceDescriptor, InterfaceAltSetting, DESCRIPTOR_TYPE_STRING,
|
||||
},
|
||||
platform,
|
||||
transfer::{
|
||||
Control, ControlIn, ControlOut, EndpointType, Queue, RequestBuffer, TransferError,
|
||||
TransferFuture,
|
||||
},
|
||||
DeviceInfo, Error,
|
||||
DeviceInfo, Error, Speed,
|
||||
};
|
||||
use log::error;
|
||||
use std::{io::ErrorKind, sync::Arc, time::Duration};
|
||||
|
|
@ -94,6 +94,18 @@ impl Device {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the device descriptor.
|
||||
///
|
||||
/// This returns cached data and does not perform IO.
|
||||
pub fn device_descriptor(&self) -> DeviceDescriptor {
|
||||
self.backend.device_descriptor()
|
||||
}
|
||||
|
||||
/// Get device speed.
|
||||
pub fn speed(&self) -> Option<Speed> {
|
||||
self.backend.speed()
|
||||
}
|
||||
|
||||
/// Get information about the active configuration.
|
||||
///
|
||||
/// This returns cached data and does not perform IO. However, it can fail if the
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use super::{
|
|||
usbfs::{self, Urb},
|
||||
SysfsPath,
|
||||
};
|
||||
use crate::descriptors::Configuration;
|
||||
use crate::descriptors::{validate_device_descriptor, Configuration, DeviceDescriptor};
|
||||
use crate::platform::linux_usbfs::events::Watch;
|
||||
use crate::transfer::{ControlType, Recipient};
|
||||
use crate::{
|
||||
|
|
@ -33,7 +33,7 @@ use crate::{
|
|||
transfer::{
|
||||
notify_completion, Control, Direction, EndpointType, TransferError, TransferHandle,
|
||||
},
|
||||
DeviceInfo, Error,
|
||||
DeviceInfo, Error, Speed,
|
||||
};
|
||||
|
||||
pub(crate) struct LinuxDevice {
|
||||
|
|
@ -84,6 +84,13 @@ impl LinuxDevice {
|
|||
buf
|
||||
};
|
||||
|
||||
let Some(_) = validate_device_descriptor(&descriptors) else {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"invalid device descriptor",
|
||||
));
|
||||
};
|
||||
|
||||
let active_config = if let Some(active_config) = active_config {
|
||||
active_config
|
||||
} else {
|
||||
|
|
@ -152,6 +159,10 @@ impl LinuxDevice {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn device_descriptor(&self) -> DeviceDescriptor {
|
||||
DeviceDescriptor::new(&self.descriptors)
|
||||
}
|
||||
|
||||
pub(crate) fn configuration_descriptors(&self) -> impl Iterator<Item = &[u8]> {
|
||||
parse_concatenated_config_descriptors(&self.descriptors[DESCRIPTOR_LEN_DEVICE as usize..])
|
||||
}
|
||||
|
|
@ -405,6 +416,21 @@ impl LinuxDevice {
|
|||
);
|
||||
return Err(ErrorKind::Other.into());
|
||||
}
|
||||
|
||||
pub(crate) fn speed(&self) -> Option<Speed> {
|
||||
usbfs::get_speed(&self.fd)
|
||||
.inspect_err(|e| log::error!("USBDEVFS_GET_SPEED failed: {e}"))
|
||||
.ok()
|
||||
.and_then(|raw_speed| match raw_speed {
|
||||
1 => Some(Speed::Low),
|
||||
2 => Some(Speed::Full),
|
||||
3 => Some(Speed::High),
|
||||
// 4 is wireless USB, but we don't support it
|
||||
5 => Some(Speed::Super),
|
||||
6 => Some(Speed::SuperPlus),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LinuxDevice {
|
||||
|
|
|
|||
|
|
@ -286,3 +286,10 @@ pub fn clear_halt<Fd: AsFd>(fd: Fd, endpoint: u8) -> io::Result<()> {
|
|||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_speed<Fd: AsFd>(fd: Fd) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let ctl = Transfer::<ioctl::NoneOpcode<b'U', 31, ()>, ()>::new(());
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@ use std::{
|
|||
use log::{debug, error};
|
||||
|
||||
use crate::{
|
||||
platform::macos_iokit::events::add_event_source,
|
||||
descriptors::DeviceDescriptor,
|
||||
platform::macos_iokit::{enumeration::device_descriptor_from_fields, events::add_event_source},
|
||||
transfer::{Control, Direction, EndpointType, TransferError, TransferHandle},
|
||||
DeviceInfo, Error,
|
||||
DeviceInfo, Error, Speed,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
|
@ -29,6 +30,8 @@ use super::{
|
|||
pub(crate) struct MacDevice {
|
||||
_event_registration: EventRegistration,
|
||||
pub(super) device: IoKitDevice,
|
||||
device_descriptor: DeviceDescriptor,
|
||||
speed: Option<Speed>,
|
||||
active_config: AtomicU8,
|
||||
is_open_exclusive: Mutex<bool>,
|
||||
claimed_interfaces: AtomicUsize,
|
||||
|
|
@ -51,7 +54,7 @@ impl MacDevice {
|
|||
pub(crate) fn from_device_info(d: &DeviceInfo) -> Result<Arc<MacDevice>, Error> {
|
||||
log::info!("Opening device from registry id {}", d.registry_id);
|
||||
let service = service_by_registry_id(d.registry_id)?;
|
||||
let device = IoKitDevice::new(service)?;
|
||||
let device = IoKitDevice::new(&service)?;
|
||||
let _event_registration = add_event_source(device.create_async_event_source()?);
|
||||
|
||||
let opened = match unsafe { call_iokit_function!(device.raw, USBDeviceOpen()) } {
|
||||
|
|
@ -64,6 +67,13 @@ impl MacDevice {
|
|||
}
|
||||
};
|
||||
|
||||
let device_descriptor = device_descriptor_from_fields(&service).ok_or_else(|| {
|
||||
Error::new(
|
||||
ErrorKind::Other,
|
||||
"could not read properties for device descriptor",
|
||||
)
|
||||
})?;
|
||||
|
||||
let active_config = if let Some(active_config) = guess_active_config(&device) {
|
||||
log::debug!("Active config from single descriptor is {}", active_config);
|
||||
active_config
|
||||
|
|
@ -76,12 +86,22 @@ impl MacDevice {
|
|||
Ok(Arc::new(MacDevice {
|
||||
_event_registration,
|
||||
device,
|
||||
device_descriptor,
|
||||
speed: d.speed,
|
||||
active_config: AtomicU8::new(active_config),
|
||||
is_open_exclusive: Mutex::new(opened),
|
||||
claimed_interfaces: AtomicUsize::new(0),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn device_descriptor(&self) -> DeviceDescriptor {
|
||||
self.device_descriptor.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn speed(&self) -> Option<Speed> {
|
||||
self.speed
|
||||
}
|
||||
|
||||
pub(crate) fn active_configuration_value(&self) -> u8 {
|
||||
self.active_config.load(Ordering::SeqCst)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ use io_kit_sys::{
|
|||
};
|
||||
use log::debug;
|
||||
|
||||
use crate::{BusInfo, DeviceInfo, Error, InterfaceInfo, Speed, UsbControllerType};
|
||||
use crate::{
|
||||
descriptors::DeviceDescriptor, BusInfo, DeviceInfo, Error, InterfaceInfo, Speed,
|
||||
UsbControllerType,
|
||||
};
|
||||
|
||||
use super::iokit::{IoService, IoServiceIterator};
|
||||
/// IOKit class name for PCI USB XHCI high-speed controllers (USB 3.0+)
|
||||
|
|
@ -268,6 +271,25 @@ fn parse_location_id(id: u32) -> Vec<u8> {
|
|||
chain
|
||||
}
|
||||
|
||||
/// There is no API in iokit to get the cached device descriptor as bytes, but
|
||||
/// we have all the fields to rebuild it exactly.
|
||||
pub(crate) fn device_descriptor_from_fields(device: &IoService) -> Option<DeviceDescriptor> {
|
||||
Some(DeviceDescriptor::from_fields(
|
||||
get_integer_property(&device, "bcdUSB")? as u16,
|
||||
get_integer_property(&device, "bDeviceClass")? as u8,
|
||||
get_integer_property(&device, "bDeviceSubClass")? as u8,
|
||||
get_integer_property(&device, "bDeviceProtocol")? as u8,
|
||||
get_integer_property(&device, "bMaxPacketSize0")? as u8,
|
||||
get_integer_property(&device, "idVendor")? as u16,
|
||||
get_integer_property(&device, "idProduct")? as u16,
|
||||
get_integer_property(&device, "bcdDevice")? as u16,
|
||||
get_integer_property(&device, "iManufacturer")? as u8,
|
||||
get_integer_property(&device, "iProduct")? as u8,
|
||||
get_integer_property(&device, "iSerialNumber")? as u8,
|
||||
get_integer_property(&device, "bNumConfigurations")? as u8,
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_location_id() {
|
||||
assert_eq!(parse_location_id(0x01234567), vec![2, 3, 4, 5, 6, 7]);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ pub(crate) struct IoKitDevice {
|
|||
|
||||
impl IoKitDevice {
|
||||
/// Get the raw USB device associated with the service.
|
||||
pub(crate) fn new(service: IoService) -> Result<IoKitDevice, Error> {
|
||||
pub(crate) fn new(service: &IoService) -> Result<IoKitDevice, Error> {
|
||||
unsafe {
|
||||
// According to the libusb maintainers, this will sometimes spuriously
|
||||
// return `kIOReturnNoResources` for reasons Apple won't explain, usually
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{
|
|||
collections::{btree_map::Entry, BTreeMap},
|
||||
ffi::c_void,
|
||||
io::{self, ErrorKind},
|
||||
mem::size_of_val,
|
||||
mem::{size_of_val, transmute},
|
||||
os::windows::{
|
||||
io::{AsRawHandle, RawHandle},
|
||||
prelude::OwnedHandle,
|
||||
|
|
@ -23,9 +23,12 @@ use windows_sys::Win32::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
descriptors::{validate_config_descriptor, DESCRIPTOR_TYPE_CONFIGURATION},
|
||||
descriptors::{
|
||||
validate_config_descriptor, DeviceDescriptor, DESCRIPTOR_LEN_DEVICE,
|
||||
DESCRIPTOR_TYPE_CONFIGURATION,
|
||||
},
|
||||
transfer::{Control, Direction, EndpointType, Recipient, TransferError, TransferHandle},
|
||||
DeviceInfo, Error,
|
||||
DeviceInfo, Error, Speed,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
|
@ -38,8 +41,10 @@ use super::{
|
|||
};
|
||||
|
||||
pub(crate) struct WindowsDevice {
|
||||
device_descriptor: DeviceDescriptor,
|
||||
config_descriptors: Vec<Vec<u8>>,
|
||||
active_config: u8,
|
||||
speed: Option<Speed>,
|
||||
devinst: DevInst,
|
||||
handles: Mutex<BTreeMap<u8, WinusbFileHandle>>,
|
||||
}
|
||||
|
|
@ -54,8 +59,15 @@ impl WindowsDevice {
|
|||
// instead.
|
||||
let hub_port = HubPort::by_child_devinst(d.devinst)?;
|
||||
let connection_info = hub_port.get_info()?;
|
||||
let num_configurations = connection_info.device_desc.bNumConfigurations;
|
||||
|
||||
// Safety: Windows API struct is repr(C), packed, and we're assuming Windows is little-endian
|
||||
let device_descriptor = unsafe {
|
||||
DeviceDescriptor::new(&transmute::<_, [u8; DESCRIPTOR_LEN_DEVICE as usize]>(
|
||||
connection_info.device_desc,
|
||||
))
|
||||
};
|
||||
|
||||
let num_configurations = connection_info.device_desc.bNumConfigurations;
|
||||
let config_descriptors = (0..num_configurations)
|
||||
.flat_map(|i| {
|
||||
let res = hub_port.get_descriptor(DESCRIPTOR_TYPE_CONFIGURATION, i, 0);
|
||||
|
|
@ -70,13 +82,23 @@ impl WindowsDevice {
|
|||
.collect();
|
||||
|
||||
Ok(Arc::new(WindowsDevice {
|
||||
device_descriptor,
|
||||
config_descriptors,
|
||||
speed: connection_info.speed,
|
||||
active_config: connection_info.active_config,
|
||||
devinst: d.devinst,
|
||||
handles: Mutex::new(BTreeMap::new()),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn device_descriptor(&self) -> DeviceDescriptor {
|
||||
self.device_descriptor.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn speed(&self) -> Option<Speed> {
|
||||
self.speed
|
||||
}
|
||||
|
||||
pub(crate) fn active_configuration_value(&self) -> u8 {
|
||||
self.active_config
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue