| use rusb::{ |
| ConfigDescriptor, DeviceDescriptor, DeviceHandle, DeviceList, EndpointDescriptor, |
| InterfaceDescriptor, Language, Result, Speed, UsbContext, |
| }; |
| use std::time::Duration; |
| |
| use usb_ids::{self, FromId}; |
| |
| struct UsbDevice<T: UsbContext> { |
| handle: DeviceHandle<T>, |
| language: Language, |
| timeout: Duration, |
| } |
| |
| fn main() { |
| list_devices().unwrap(); |
| } |
| |
| fn list_devices() -> Result<()> { |
| let timeout = Duration::from_secs(1); |
| |
| for device in DeviceList::new()?.iter() { |
| 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!( |
| "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); |
| |
| 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<T: UsbContext>(device_desc: &DeviceDescriptor, handle: &mut Option<UsbDevice<T>>) { |
| 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!(" bLength {:3}", device_desc.length()); |
| println!(" bDescriptorType {:3}", device_desc.descriptor_type()); |
| 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<T: UsbContext>(config_desc: &ConfigDescriptor, handle: &mut Option<UsbDevice<T>>) { |
| println!(" Config Descriptor:"); |
| println!(" bLength {:3}", config_desc.length()); |
| println!( |
| " bDescriptorType {:3}", |
| config_desc.descriptor_type() |
| ); |
| println!(" wTotalLength {:#06x}", config_desc.total_length()); |
| 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<T: UsbContext>( |
| interface_desc: &InterfaceDescriptor, |
| handle: &mut Option<UsbDevice<T>>, |
| ) { |
| println!(" Interface Descriptor:"); |
| println!(" bLength {:3}", interface_desc.length()); |
| println!( |
| " bDescriptorType {:3}", |
| interface_desc.descriptor_type() |
| ); |
| 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!(" bLength {:3}", endpoint_desc.length()); |
| println!( |
| " bDescriptorType {:3}", |
| endpoint_desc.descriptor_type() |
| ); |
| println!( |
| " bEndpointAddress {:#04x} EP {} {:?}", |
| endpoint_desc.address(), |
| endpoint_desc.number(), |
| endpoint_desc.direction() |
| ); |
| println!(" bmAttributes:"); |
| println!( |
| " Transfer Type {:?}", |
| endpoint_desc.transfer_type() |
| ); |
| println!( |
| " Synch Type {:?}", |
| endpoint_desc.sync_type() |
| ); |
| println!( |
| " Usage Type {:?}", |
| endpoint_desc.usage_type() |
| ); |
| println!( |
| " wMaxPacketSize {:#06x}", |
| endpoint_desc.max_packet_size() |
| ); |
| println!( |
| " bInterval {:3}", |
| endpoint_desc.interval() |
| ); |
| } |
| |
| fn get_speed(speed: Speed) -> &'static str { |
| match speed { |
| Speed::SuperPlus => "10000 Mbps", |
| Speed::Super => "5000 Mbps", |
| Speed::High => " 480 Mbps", |
| Speed::Full => " 12 Mbps", |
| Speed::Low => " 1.5 Mbps", |
| _ => "(unknown)", |
| } |
| } |