use std::time::Duration; use yusb::{ ConfigDescriptor, DeviceDescriptor, DeviceHandle, DeviceList, EndpointDescriptor, InterfaceDescriptor, Language, Result, TransferType, }; use usb_ids::{self, FromId}; struct UsbDevice { handle: DeviceHandle, language: Language, timeout: Duration, } fn main() { if let Err(err) = list_devices() { eprintln!("{}", err); } } fn list_devices() -> Result<()> { let timeout = Duration::from_secs(1); for device in DeviceList::new()? { let device_desc = match device.device_descriptor() { Ok(d) => d, Err(_) => continue, }; let mut usb_device = { match device.open() { Ok(h) => match h.read_languages(timeout) { Ok(l) => { if !l.is_empty() { Some(UsbDevice { handle: h, language: l[0], timeout, }) } else { None } } Err(_) => None, }, Err(_) => None, } }; println!( "{:?} Port {} {:?} Speed ({})", device, device.port().map(|p| p.to_string()).unwrap_or_default(), device.speed(), device.speed() ); print_device(&device_desc, &mut usb_device); for n in 0..device_desc.num_configurations() { let config_desc = match device.config_descriptor(n) { Ok(c) => c, Err(_) => continue, }; print_config(&config_desc, &mut usb_device); for interface in config_desc.interfaces() { for interface_desc in interface.descriptors() { print_interface(&interface_desc, &mut usb_device); for endpoint_desc in interface_desc.endpoint_descriptors() { print_endpoint(&endpoint_desc); } } } } } Ok(()) } fn print_device(device_desc: &DeviceDescriptor, handle: &mut Option) { let vid = device_desc.vendor_id(); let pid = device_desc.product_id(); let vendor_name = match usb_ids::Vendor::from_id(device_desc.vendor_id()) { Some(vendor) => vendor.name(), None => "Unknown vendor", }; let product_name = match usb_ids::Device::from_vid_pid(device_desc.vendor_id(), device_desc.product_id()) { Some(product) => product.name(), None => "Unknown product", }; println!("Device Descriptor:"); println!( " bcdUSB {:2}.{}{}", device_desc.usb_version().major(), device_desc.usb_version().minor(), device_desc.usb_version().sub_minor() ); println!(" bDeviceClass {:#04x}", device_desc.class_code()); println!( " bDeviceSubClass {:#04x}", device_desc.sub_class_code() ); println!(" bDeviceProtocol {:#04x}", device_desc.protocol_code()); println!(" bMaxPacketSize0 {:3}", device_desc.max_packet_size()); println!(" idVendor {vid:#06x} {vendor_name}",); println!(" idProduct {pid:#06x} {product_name}",); println!( " bcdDevice {:2}.{}{}", device_desc.device_version().major(), device_desc.device_version().minor(), device_desc.device_version().sub_minor() ); println!( " iManufacturer {:3} {}", device_desc.manufacturer_string_index().unwrap_or(0), handle.as_mut().map_or(String::new(), |h| h .handle .read_manufacturer_string(h.language, device_desc, h.timeout) .unwrap_or_default()) ); println!( " iProduct {:3} {}", device_desc.product_string_index().unwrap_or(0), handle.as_mut().map_or(String::new(), |h| h .handle .read_product_string(h.language, device_desc, h.timeout) .unwrap_or_default()) ); println!( " iSerialNumber {:3} {}", device_desc.serial_number_string_index().unwrap_or(0), handle.as_mut().map_or(String::new(), |h| h .handle .read_serial_number_string(h.language, device_desc, h.timeout) .unwrap_or_default()) ); println!( " bNumConfigurations {:3}", device_desc.num_configurations() ); } fn print_config(config_desc: &ConfigDescriptor, handle: &mut Option) { println!(" Config Descriptor:"); println!( " bNumInterfaces {:3}", config_desc.num_interfaces() ); println!(" bConfigurationValue {:3}", config_desc.number()); println!( " iConfiguration {:3} {}", config_desc.description_string_index().unwrap_or(0), handle.as_mut().map_or(String::new(), |h| h .handle .read_configuration_string(h.language, config_desc, h.timeout) .unwrap_or_default()) ); println!(" bmAttributes:"); println!(" Self Powered {:>5}", config_desc.self_powered()); println!(" Remote Wakeup {:>5}", config_desc.remote_wakeup()); println!(" bMaxPower {:4}mW", config_desc.max_power()); if !config_desc.extra().is_empty() { println!(" {:?}", config_desc.extra()); } else { println!(" no extra data"); } } fn print_interface(interface_desc: &InterfaceDescriptor, handle: &mut Option) { println!(" Interface Descriptor:"); println!( " bInterfaceNumber {:3}", interface_desc.interface_number() ); println!( " bAlternateSetting {:3}", interface_desc.setting_number() ); println!( " bNumEndpoints {:3}", interface_desc.num_endpoints() ); println!( " bInterfaceClass {:#04x}", interface_desc.class_code() ); println!( " bInterfaceSubClass {:#04x}", interface_desc.sub_class_code() ); println!( " bInterfaceProtocol {:#04x}", interface_desc.protocol_code() ); println!( " iInterface {:3} {}", interface_desc.description_string_index().unwrap_or(0), handle.as_mut().map_or(String::new(), |h| h .handle .read_interface_string(h.language, interface_desc, h.timeout) .unwrap_or_default()) ); if interface_desc.extra().is_empty() { println!(" {:?}", interface_desc.extra()); } else { println!(" no extra data"); } } fn print_endpoint(endpoint_desc: &EndpointDescriptor) { println!(" Endpoint Descriptor:"); println!( " bEndpointAddress {:#04x} EP {} {:?}", endpoint_desc.address(), endpoint_desc.number(), endpoint_desc.direction() ); println!(" bmAttributes:"); println!( " Transfer Type {:?}", endpoint_desc.transfer_type() ); if endpoint_desc.transfer_type() == TransferType::Isochronous { println!( " Iso Sync Type {:?}", endpoint_desc.iso_sync_type() ); println!( " Iso Usage Type {:?}", endpoint_desc.iso_usage_type() ); } println!( " wMaxPacketSize {:#06x}", endpoint_desc.max_packet_size() ); println!( " bInterval {:3}", endpoint_desc.interval() ); }