From d082055883ce4843addf8d24575de14956b9d707 Mon Sep 17 00:00:00 2001 From: qianchenzhumeng Date: Thu, 18 Nov 2021 22:26:28 +0800 Subject: [PATCH] adds function function to get BOS descriptor --- examples/list_devices.rs | 60 ++++++++ src/bos_descriptor.rs | 304 +++++++++++++++++++++++++++++++++++++++ src/device_handle.rs | 113 +++++++++++++++ src/lib.rs | 3 +- src/test_helpers.rs | 15 ++ 5 files changed, 494 insertions(+), 1 deletion(-) create mode 100644 src/bos_descriptor.rs diff --git a/examples/list_devices.rs b/examples/list_devices.rs index 4169c3e..fec45f6 100644 --- a/examples/list_devices.rs +++ b/examples/list_devices.rs @@ -50,6 +50,8 @@ fn list_devices() -> libusb::Result<()> { println!("Bus {:03} Device {:03} ID {:04x}:{:04x} {}", device.bus_number(), device.address(), device_desc.vendor_id(), device_desc.product_id(), get_speed(device.speed())); print_device(&device_desc, &mut usb_device); + print_bos(&mut usb_device); + for n in 0..device_desc.num_configurations() { let config_desc = match device.config_descriptor(n) { Ok(c) => c, @@ -95,6 +97,64 @@ fn print_device(device_desc: &libusb::DeviceDescriptor, handle: &mut Option) { + let h = match UsbDevice { + Some(h) => &h.handle, + None => return, + }; + let bos_desc = match h.bos_descriptor() { + Ok(desc) => desc, + Err(_) => { return; }, + + }; + + println!(" BOS Descriptor:"); + println!(" bDescriptorType {}", bos_desc.descriptor_type()); + println!(" bNumDeviceCaps {}", bos_desc.num_device_caps()); + + for dev_cap in bos_desc.dev_capability() { + match dev_cap.dev_capability_type() { + 2 => { + match h.bos_usb_2_0_extension_descriptor(&bos_desc, &dev_cap) { + Ok(desc) => { + println!(" USB 2.0 Extension Capabilities:"); + println!(" bDevCapabilityType: {}", desc.dev_capability_type()); + println!(" bmAttributes {:08x}h", desc.attributes()); + }, + Err(e) => println!("{}", e), + } + }, + 3 => { + match h.bos_superspeed_usb_descriptor(&bos_desc, &dev_cap) { + Ok(desc) => { + println!(" USB 3.0 Capabilities:"); + println!(" bDevCapabilityType: {}", desc.dev_capability_type()); + println!(" bmAttributes: {:02x}h", desc.attributes()); + println!(" wSpeedSupported: {}", desc.speed_supported()); + println!(" bFunctionalitySupport: {}", desc.functionality_support()); + println!(" bU1devExitLat: {}", desc.u1_dev_exit_lat()); + println!(" bU2devExitLat: {}", desc.u2_dev_exit_lat()); + }, + Err(e) => println!("{}", e), + } + }, + 4 => { + match h.bos_container_id_descriptor(&bos_desc, &dev_cap) { + Ok(desc) => { + print!(" Container ID: "); + for i in desc.container_id() { + print!("{:02X}", i); + } + println!(); + }, + Err(e) => println!("{}", e), + } + }, + _ => {}, + } + } +} + fn print_config(config_desc: &libusb::ConfigDescriptor, handle: &mut Option) { println!(" Config Descriptor:"); println!(" bNumInterfaces {:3}", config_desc.num_interfaces()); diff --git a/src/bos_descriptor.rs b/src/bos_descriptor.rs new file mode 100644 index 0000000..4f4a8df --- /dev/null +++ b/src/bos_descriptor.rs @@ -0,0 +1,304 @@ +use std::fmt; +use std::mem; +use libusb::*; + +pub struct BosDescriptor { + descriptor: *const libusb_bos_descriptor, +} + +pub struct BosDevCapabilityDescriptor { + addr: *const *const u8, + descriptor: *const libusb_bos_dev_capability_descriptor, +} + +pub struct Usb20ExtensionDescriptor { + descriptor: *const libusb_usb_2_0_extension_descriptor, +} + +pub struct SsUsbDescriptor { + descriptor: *const libusb_ss_usb_device_capability_descriptor, +} + +pub struct ContainerIdDescriptor { + descriptor: *const libusb_container_id_descriptor, +} + +impl Drop for BosDescriptor { + fn drop(&mut self) { + unsafe { + libusb_free_bos_descriptor(self.descriptor as *mut libusb_bos_descriptor); + } + } +} + +impl Drop for Usb20ExtensionDescriptor { + fn drop(&mut self) { + unsafe { + libusb_free_usb_2_0_extension_descriptor(self.descriptor as *mut libusb_usb_2_0_extension_descriptor); + } + } +} + +impl Drop for SsUsbDescriptor { + fn drop(&mut self) { + unsafe { + libusb_free_ss_usb_device_capability_descriptor(self.descriptor as *mut libusb_ss_usb_device_capability_descriptor); + } + } +} + +impl Drop for ContainerIdDescriptor { + fn drop(&mut self) { + unsafe { + libusb_free_container_id_descriptor(self.descriptor as *mut libusb_container_id_descriptor); + } + } +} + +/// BOS Descriptor +impl BosDescriptor { + pub fn length(&self) -> u8 { + unsafe { + (*self.descriptor).bLength + } + } + + pub fn descriptor_type(&self) -> u8 { + unsafe { + (*self.descriptor).bDescriptorType + } + } + + pub fn total_length(&self) -> u16 { + unsafe { + (*self.descriptor).wTotalLength + } + } + + pub fn num_device_caps(&self) -> u8 { + unsafe { + (*self.descriptor).bNumDeviceCaps + } + } + + pub fn dev_capability(&self) -> Vec { + unsafe { + let mut v: Vec = Vec::new(); + for i in 0..self.num_device_caps() { + // 先转换成指针 + let point = std::ptr::addr_of!((*self.descriptor).dev_capability).offset(i as _); + // 在将指针转换为 *const *const libusb_bos_dev_capability_descriptor,然后解引用两次 + let dev_cap = &(*(*(point as * const *const libusb_bos_dev_capability_descriptor))); + let cap = from_libusb_bos_dev_capability_descriptor(point as *const *const u8, dev_cap); + v.push(cap); + } + v + } + } +} + +/// Device Capability Descriptor +impl BosDevCapabilityDescriptor { + pub fn get_addr(&self) -> *const *const u8 { + self.addr + } + + pub fn length(&self) -> u8 { + unsafe { + (*self.descriptor).bLength + } + } + + pub fn descriptor_type(&self) -> u8 { + unsafe { + (*self.descriptor).bDescriptorType + } + } + + pub fn dev_capability_type(&self) -> u8 { + unsafe { + (*self.descriptor).bDevCapabilityType + } + } +} + +/// USB 2.0 Extension Descriptor +impl Usb20ExtensionDescriptor { + pub fn length(&self) -> u8 { + unsafe { + (*self.descriptor).bLength + } + } + + pub fn descriptor_type(&self) -> u8 { + unsafe { + (*self.descriptor).bDescriptorType + } + } + + pub fn dev_capability_type(&self) -> u8 { + unsafe { + (*self.descriptor).bDevCapabilityType + } + } + + pub fn attributes(&self) -> u32 { + unsafe { + (*self.descriptor).bmAttributes + } + } +} + +/// SuperSpeed USB Descriptor +impl SsUsbDescriptor { + pub fn length(&self) -> u8 { + unsafe { + (*self.descriptor).bLength + } + } + + pub fn descriptor_type(&self) -> u8 { + unsafe { + (*self.descriptor).bDescriptorType + } + } + + pub fn dev_capability_type(&self) -> u8 { + unsafe { + (*self.descriptor).bDevCapabilityType + } + } + + pub fn attributes(&self) -> u8 { + unsafe { + (*self.descriptor).bmAttributes + } + } + + pub fn speed_supported(&self) -> u16 { + unsafe { + (*self.descriptor).wSpeedSupported + } + } + + pub fn functionality_support(&self) -> u8 { + unsafe { + (*self.descriptor).bFunctionalitySupport + } + } + + pub fn u1_dev_exit_lat(&self) -> u8 { + unsafe { + (*self.descriptor).bU1DevExitLat + } + } + + pub fn u2_dev_exit_lat(&self) -> u8 { + unsafe { + (*self.descriptor).bU2DevExitLat + } + } +} + +/// Container ID Descriptor +impl ContainerIdDescriptor { + pub fn length(&self) -> u8 { + unsafe { + (*self.descriptor).bLength + } + } + + pub fn descriptor_type(&self) -> u8 { + unsafe { + (*self.descriptor).bDescriptorType + } + } + + pub fn dev_capability_type(&self) -> u8 { + unsafe { + (*self.descriptor).bDevCapabilityType + } + } + + pub fn reserved(&self) -> u8 { + unsafe { + (*self.descriptor).bReserved + } + } + + pub fn container_id(&self) -> [u8; 16] { + unsafe { + (*self.descriptor).ContainerId + } + } +} + +impl fmt::Debug for BosDescriptor { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let mut debug = f.debug_struct("BosDescriptor"); + + let descriptor: &libusb_bos_descriptor = unsafe { + mem::transmute(self.descriptor) + }; + + debug.field("bLength", &descriptor.bLength); + debug.field("bDescriptorType", &descriptor.bDescriptorType); + debug.field("wTotalLength", &descriptor.wTotalLength); + debug.field("bNumDeviceCaps", &descriptor.bNumDeviceCaps); + + debug.finish() + } +} + +#[doc(hidden)] +pub fn from_libusb(bos: *const libusb_bos_descriptor) -> BosDescriptor { + BosDescriptor { descriptor: bos } +} + +#[doc(hidden)] +pub fn from_libusb_bos_dev_capability_descriptor(addr: *const *const u8, bos: *const libusb_bos_dev_capability_descriptor) -> BosDevCapabilityDescriptor { + BosDevCapabilityDescriptor { addr: addr, descriptor: bos } +} + +#[doc(hidden)] +pub fn from_libusb_usb_2_0_extension_descriptor(bos: *const libusb_usb_2_0_extension_descriptor) -> Usb20ExtensionDescriptor { + Usb20ExtensionDescriptor { descriptor: bos } +} + +#[doc(hidden)] +pub fn from_libusb_ss_usb_device_capability_descriptor(bos: *const libusb_ss_usb_device_capability_descriptor) -> SsUsbDescriptor { + SsUsbDescriptor { descriptor: bos } +} + +#[doc(hidden)] +pub fn from_libusb_container_id_descriptor(bos: *const libusb_container_id_descriptor) -> ContainerIdDescriptor { + ContainerIdDescriptor { descriptor: bos } +} + +#[cfg(test)] +mod test { + use std::mem; + + // The Drop trait impl calls libusb_free_config_descriptor(), which would attempt to free + // unallocated memory for a stack-allocated config descriptor. Allocating a config descriptor + // is not a simple malloc()/free() inside libusb. Mimicking libusb's allocation would be + // error-prone, difficult to maintain, and provide little benefit for the tests. It's easier to + // use mem::forget() to prevent the Drop trait impl from running. The config descriptor passed + // as `$config` should be stack-allocated to prevent memory leaks in the test suite. + macro_rules! with_bos { + ($name:ident : $bos:expr => $body:block) => { + { + let $name = unsafe { super::from_libusb(&$bos) }; + $body; + mem::forget($name); + } + } + } + + #[test] + fn it_has_num_device_caps() { + with_bos!(bos: bos_descriptor!(bNumDeviceCaps: 3) => { + assert_eq!(3, bos.num_device_caps()); + }); + } +} diff --git a/src/device_handle.rs b/src/device_handle.rs index eb9c360..9ba4b66 100644 --- a/src/device_handle.rs +++ b/src/device_handle.rs @@ -11,6 +11,7 @@ use context::Context; use error::{self, Error}; use device_descriptor::DeviceDescriptor; use config_descriptor::ConfigDescriptor; +use bos_descriptor::{self, BosDescriptor, BosDevCapabilityDescriptor, Usb20ExtensionDescriptor, SsUsbDescriptor, ContainerIdDescriptor}; use interface_descriptor::InterfaceDescriptor; use fields::{Direction, RequestType, Recipient, request_type}; use language::Language; @@ -491,6 +492,118 @@ impl<'a> DeviceHandle<'a> { Some(n) => self.read_string_descriptor(language, n, timeout) } } + + /// Reads a BOS descriptor. + pub fn bos_descriptor(&self) -> ::Result { + let mut descriptor: * const libusb_bos_descriptor = unsafe{ mem::uninitialized() }; + + match unsafe { libusb_get_bos_descriptor(self.handle, &mut descriptor) } { + 0 => Ok(bos_descriptor::from_libusb(descriptor)), + err => Err(error::from_libusb(err)), + } + } + + /// Reads a USB2.0 extension descriptor. + pub fn bos_usb_2_0_extension_descriptor(&self, bos_desc: &BosDescriptor, bos_dev_cap_desc: &BosDevCapabilityDescriptor) -> ::Result{ + let context: *mut libusb_context = unsafe{ mem::uninitialized() }; + let mut usb_2_0_extension: * const libusb_usb_2_0_extension_descriptor = unsafe{ mem::uninitialized() }; + // 结构体和其成员 dev_capability_data 指向的内存是连续的,因此,只能将原来的内存区域传下去,不能新封装结构体。 + //查找 dev_cap 的地址: + let mut cap_addr: Option<*const u8> = None; + for cap in bos_desc.dev_capability() { + if cap.dev_capability_type() == LIBUSB_BT_USB_2_0_EXTENSION { + unsafe { + cap_addr = Some(*cap.get_addr()); + } + } + } + unsafe { + match cap_addr { + None => { + return Err(error::from_libusb(LIBUSB_ERROR_INVALID_PARAM)); + }, + Some(addr) => { + let dev_cap: *mut libusb_bos_dev_capability_descriptor = addr as *mut libusb::libusb_bos_dev_capability_descriptor; + match libusb_get_usb_2_0_extension_descriptor( + context, + dev_cap, + &mut usb_2_0_extension) + { + 0 => Ok(bos_descriptor::from_libusb_usb_2_0_extension_descriptor(usb_2_0_extension)), + err => Err(error::from_libusb(err)), + } + } + } + } + } + + /// Reads a SuperSpeed USB descriptor. + pub fn bos_superspeed_usb_descriptor(&self, bos_desc: &BosDescriptor, bos_dev_cap_desc: &BosDevCapabilityDescriptor) -> ::Result{ + let context: *mut libusb_context = unsafe{ mem::uninitialized() }; + let mut ss_usb_device_cap: * const libusb_ss_usb_device_capability_descriptor = unsafe{ mem::uninitialized() }; + // 结构体和其成员 dev_capability_data 指向的内存是连续的,因此,只能将原来的内存区域传下去,不能新封装结构体。 + //查找 dev_cap 的地址: + let mut cap_addr: Option<*const u8> = None; + for cap in bos_desc.dev_capability() { + if cap.dev_capability_type() == LIBUSB_BT_SS_USB_DEVICE_CAPABILITY { + unsafe { + cap_addr = Some(*cap.get_addr()); + } + } + } + unsafe { + match cap_addr { + None => { + return Err(error::from_libusb(LIBUSB_ERROR_INVALID_PARAM)); + }, + Some(addr) => { + let dev_cap: *mut libusb_bos_dev_capability_descriptor = addr as *mut libusb::libusb_bos_dev_capability_descriptor; + match libusb_get_ss_usb_device_capability_descriptor( + context, + dev_cap, + &mut ss_usb_device_cap) + { + 0 => Ok(bos_descriptor::from_libusb_ss_usb_device_capability_descriptor(ss_usb_device_cap)), + err => Err(error::from_libusb(err)), + } + } + } + } + } + + /// Reads a container ID extension descriptor. + pub fn bos_container_id_descriptor(&self, bos_desc: &BosDescriptor, bos_dev_cap_desc: &BosDevCapabilityDescriptor) -> ::Result{ + let context: *mut libusb_context = unsafe{ mem::uninitialized() }; + let mut container_id: * const libusb_container_id_descriptor = unsafe{ mem::uninitialized() }; + // 结构体和其成员 dev_capability_data 指向的内存是连续的,因此,只能将原来的内存区域传下去,不能新封装结构体。 + //查找 dev_cap 的地址: + let mut cap_addr: Option<*const u8> = None; + for cap in bos_desc.dev_capability() { + if cap.dev_capability_type() == LIBUSB_BT_CONTAINER_ID { + unsafe { + cap_addr = Some(*cap.get_addr()); + } + } + } + unsafe { + match cap_addr { + None => { + return Err(error::from_libusb(LIBUSB_ERROR_INVALID_PARAM)); + }, + Some(addr) => { + let dev_cap: *mut libusb_bos_dev_capability_descriptor = addr as *mut libusb::libusb_bos_dev_capability_descriptor; + match libusb_get_container_id_descriptor( + context, + dev_cap, + &mut container_id) + { + 0 => Ok(bos_descriptor::from_libusb_container_id_descriptor(container_id)), + err => Err(error::from_libusb(err)), + } + } + } + } + } } #[doc(hidden)] diff --git a/src/lib.rs b/src/lib.rs index 55cb3b4..08ef82c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,11 +15,11 @@ pub use device_handle::DeviceHandle; pub use fields::{Speed, TransferType, SyncType, UsageType, Direction, RequestType, Recipient, Version, request_type}; pub use device_descriptor::DeviceDescriptor; pub use config_descriptor::{ConfigDescriptor, Interfaces}; +pub use bos_descriptor::{BosDescriptor, BosDevCapabilityDescriptor}; pub use interface_descriptor::{Interface, InterfaceDescriptors, InterfaceDescriptor, EndpointDescriptors}; pub use endpoint_descriptor::EndpointDescriptor; pub use language::{Language, PrimaryLanguage, SubLanguage}; - #[cfg(test)] #[macro_use] mod test_helpers; @@ -36,6 +36,7 @@ mod device_handle; mod fields; mod device_descriptor; mod config_descriptor; +mod bos_descriptor; mod interface_descriptor; mod endpoint_descriptor; mod language; diff --git a/src/test_helpers.rs b/src/test_helpers.rs index 4dcf9ff..9e7e02d 100644 --- a/src/test_helpers.rs +++ b/src/test_helpers.rs @@ -162,3 +162,18 @@ macro_rules! device_descriptor { ) } } + +#[macro_export] +macro_rules! bos_descriptor { + ($($key:ident : $value:expr),*) => { + merge!( + ::libusb::libusb_bos_descriptor { + bLength: 5, + bDescriptorType: 0x0f, + wTotalLength: 5, + bNumDeviceCaps: 0, + dev_capability: std::ptr::null(), + } => $($key: $value),* + ) + } +}