extern crate libusb_sys as ffi; extern crate libc; use libc::{c_int,c_uchar}; use std::mem; use std::ptr; use std::slice; fn main() { let mut context: *mut ::ffi::libusb_context = unsafe { mem::uninitialized() }; match unsafe { ::ffi::libusb_init(&mut context) } { 0 => (), e => panic!("libusb_init: {}", get_error(e)) }; list_devices(context); unsafe { ::ffi::libusb_exit(context) }; } fn list_devices(context: *mut ::ffi::libusb_context) { let mut device_list: *const *mut ::ffi::libusb_device = unsafe { mem::uninitialized() }; let len = unsafe { ::ffi::libusb_get_device_list(context, &mut device_list) }; if len < 0 { println!("libusb_get_device_list: {}", get_error(len as c_int)); return; } let devs = unsafe { slice::from_raw_parts(device_list, len as usize) }; for dev in devs { display_device(dev); } unsafe { ::ffi::libusb_free_device_list(device_list, 1) }; } fn display_device(dev: &*mut ::ffi::libusb_device) { let mut descriptor: ::ffi::libusb_device_descriptor = unsafe { mem::uninitialized() }; let mut handle: *mut ::ffi::libusb_device_handle = ptr::null_mut(); let bus = unsafe { ::ffi::libusb_get_bus_number(*dev) }; let address = unsafe { ::ffi::libusb_get_device_address(*dev) }; let speed = unsafe { ::ffi::libusb_get_device_speed(*dev) }; let has_descriptor = match unsafe { ::ffi::libusb_get_device_descriptor(*dev, &mut descriptor) } { 0 => true, _ => false }; if unsafe { ::ffi::libusb_open(*dev, &mut handle) } < 0 { println!("Couldn't open device, some information will be missing"); handle = ptr::null_mut(); } print!("Bus {:03} Device {:03}", bus, address); if has_descriptor { print!(" ID {:04x}:{:04x}", descriptor.idVendor, descriptor.idProduct); } print!(" {}", get_device_speed(speed)); if has_descriptor { if descriptor.iManufacturer > 0 { match get_string_descriptor(handle, descriptor.iManufacturer) { Some(s) => print!(" {}", s), None => () } } if descriptor.iProduct > 0 { match get_string_descriptor(handle, descriptor.iProduct) { Some(s) => print!(" {}", s), None => () } } if descriptor.iSerialNumber > 0 { match get_string_descriptor(handle, descriptor.iSerialNumber) { Some(s) => print!(" Serial No. {}", s), None => () } } } println!(""); if has_descriptor { print_device_descriptor(handle, &descriptor); for i in 0..descriptor.bNumConfigurations { let mut descriptor: *const ::ffi::libusb_config_descriptor = unsafe { mem::uninitialized() }; match unsafe { ::ffi::libusb_get_config_descriptor(*dev, i, &mut descriptor) } { 0 => { let config = unsafe { &*descriptor }; let interfaces = unsafe { slice::from_raw_parts(config.interface, config.bNumInterfaces as usize) }; print_config_descriptor(handle, config); for iface in interfaces { let iface_descriptors = unsafe { slice::from_raw_parts(iface.altsetting, iface.num_altsetting as usize) }; for iface_desc in iface_descriptors { print_interface_descriptor(handle, iface_desc); let endpoints = unsafe { slice::from_raw_parts(iface_desc.endpoint, iface_desc.bNumEndpoints as usize) }; for endpoint in endpoints { print_endpoint_descriptor(endpoint); } } } unsafe { ::ffi::libusb_free_config_descriptor(descriptor) }; }, _ => () } } } if !handle.is_null() { unsafe { ::ffi::libusb_close(handle) }; } } fn print_device_descriptor(handle: *mut ::ffi::libusb_device_handle, descriptor: &::ffi::libusb_device_descriptor) { println!("Device Descriptor:"); println!(" bLength: {:16}", descriptor.bLength); println!(" bDescriptorType: {:8} {}", descriptor.bDescriptorType, get_descriptor_type(descriptor.bDescriptorType)); println!(" bcdUSB: {:#06x} {}", descriptor.bcdUSB, get_bcd_version(descriptor.bcdUSB)); println!(" bDeviceClass: {:#04x} {}", descriptor.bDeviceClass, get_class_type(descriptor.bDeviceClass)); println!(" bDeviceSubClass: {:8}", descriptor.bDeviceSubClass); println!(" bDeviceProtocol: {:8}", descriptor.bDeviceProtocol); println!(" bMaxPacketSize0: {:8}", descriptor.bMaxPacketSize0); println!(" idVendor: {:#06x}", descriptor.idVendor); println!(" idProduct: {:#06x}", descriptor.idProduct); println!(" bcdDevice: {:#06x}", descriptor.bcdDevice); println!(" iManufacturer: {:10} {}", descriptor.iManufacturer, get_string_descriptor(handle, descriptor.iManufacturer).unwrap_or(String::new())); println!(" iProduct: {:15} {}", descriptor.iProduct, get_string_descriptor(handle, descriptor.iProduct).unwrap_or(String::new())); println!(" iSerialNumber: {:10} {}", descriptor.iSerialNumber, get_string_descriptor(handle, descriptor.iSerialNumber).unwrap_or(String::new())); println!(" bNumConfigurations: {:5}", descriptor.bNumConfigurations); } fn print_config_descriptor(handle: *mut ::ffi::libusb_device_handle, descriptor: &::ffi::libusb_config_descriptor) { println!(" Configuration Descriptor:"); println!(" bLength: {:16}", descriptor.bLength); println!(" bDescriptorType: {:8} {}", descriptor.bDescriptorType, get_descriptor_type(descriptor.bDescriptorType)); println!(" wTotalLength: {:11}", descriptor.wTotalLength); println!(" bNumInterfaces: {:9}", descriptor.bNumInterfaces); println!(" bConfigurationValue: {:4}", descriptor.bConfigurationValue); println!(" iConfiguration: {:9} {}", descriptor.iConfiguration, get_string_descriptor(handle, descriptor.iConfiguration).unwrap_or(String::new())); println!(" bmAttributes: {:#04x}", descriptor.bmAttributes); println!(" bMaxPower: {:14} {}", descriptor.bMaxPower, get_max_power(descriptor.bMaxPower)); if descriptor.extra_length > 0 { let extra = unsafe { slice::from_raw_parts(descriptor.extra, descriptor.extra_length as usize) }; println!(" (extra: {:?})", extra); } } fn print_interface_descriptor(handle: *mut ::ffi::libusb_device_handle, descriptor: &::ffi::libusb_interface_descriptor) { println!(" Interface Descriptor:"); println!(" bLength: {:16}", descriptor.bLength); println!(" bDescriptorType: {:8} {}", descriptor.bDescriptorType, get_descriptor_type(descriptor.bDescriptorType)); println!(" bInterfaceNumber: {:7}", descriptor.bInterfaceNumber); println!(" bAlternateSetting: {:6}", descriptor.bAlternateSetting); println!(" bNumEndpoints: {:10}", descriptor.bNumEndpoints); println!(" bInterfaceClass: {:#04x} {}", descriptor.bInterfaceClass, get_class_type(descriptor.bInterfaceClass)); println!(" bInterfaceSubClass: {:5}", descriptor.bInterfaceSubClass); println!(" bInterfaceProtocol: {:5}", descriptor.bInterfaceProtocol); println!(" iInterface: {:13} {}", descriptor.iInterface, get_string_descriptor(handle, descriptor.iInterface).unwrap_or(String::new())); if descriptor.extra_length > 0 { let extra = unsafe { slice::from_raw_parts(descriptor.extra, descriptor.extra_length as usize) }; println!(" (extra: {:?})", extra); } } fn print_endpoint_descriptor(descriptor: &::ffi::libusb_endpoint_descriptor) { println!(" Endpoint Descriptor:"); println!(" bLength: {:16}", descriptor.bLength); println!(" bDescriptorType: {:8} {}", descriptor.bDescriptorType, get_descriptor_type(descriptor.bDescriptorType)); println!(" bEndpointAddress: {:#04x} {}", descriptor.bEndpointAddress, get_endpoint(descriptor.bEndpointAddress)); println!(" bmAttributes: {:#04x}", descriptor.bmAttributes); println!(" Transfer Type: {}", get_transfer_type(descriptor.bmAttributes)); println!(" Synch Type: {}", get_synch_type(descriptor.bmAttributes)); println!(" Usage Type: {}", get_usage_type(descriptor.bmAttributes)); println!(" wMaxPacketSize: {:#06x}", descriptor.wMaxPacketSize); println!(" bInterval: {:14}", descriptor.bInterval); println!(" bRefresh: {:15}", descriptor.bRefresh); println!(" bSynchAddress: {:10}", descriptor.bSynchAddress); if descriptor.extra_length > 0 { let extra = unsafe { slice::from_raw_parts(descriptor.extra, descriptor.extra_length as usize) }; println!(" (extra: {:?})", extra); } } fn get_error(err: c_int) -> &'static str { match err { ::ffi::LIBUSB_SUCCESS => "success", ::ffi::LIBUSB_ERROR_IO => "I/O error", ::ffi::LIBUSB_ERROR_INVALID_PARAM => "invalid parameter", ::ffi::LIBUSB_ERROR_ACCESS => "access denied", ::ffi::LIBUSB_ERROR_NO_DEVICE => "no such device", ::ffi::LIBUSB_ERROR_NOT_FOUND => "entity not found", ::ffi::LIBUSB_ERROR_BUSY => "resource busy", ::ffi::LIBUSB_ERROR_TIMEOUT => "opteration timed out", ::ffi::LIBUSB_ERROR_OVERFLOW => "overflow error", ::ffi::LIBUSB_ERROR_PIPE => "pipe error", ::ffi::LIBUSB_ERROR_INTERRUPTED => "system call interrupted", ::ffi::LIBUSB_ERROR_NO_MEM => "insufficient memory", ::ffi::LIBUSB_ERROR_NOT_SUPPORTED => "operation not supported", ::ffi::LIBUSB_ERROR_OTHER | _ => "other error" } } fn get_device_speed(speed: c_int) -> &'static str { match speed { ::ffi::LIBUSB_SPEED_SUPER => "5000 Mbps", ::ffi::LIBUSB_SPEED_HIGH => " 480 Mbps", ::ffi::LIBUSB_SPEED_FULL => " 12 Mbps", ::ffi::LIBUSB_SPEED_LOW => " 1.5 Mbps", ::ffi::LIBUSB_SPEED_UNKNOWN | _ => "(unknown)" } } fn get_max_power(power: u8) -> String { if power > 0 { format!("{}mW", power as usize * 2) } else { String::new() } } fn get_descriptor_type(desc_type: u8) -> &'static str { match desc_type { ::ffi::LIBUSB_DT_DEVICE => "Device", ::ffi::LIBUSB_DT_CONFIG => "Configuration", ::ffi::LIBUSB_DT_STRING => "String", ::ffi::LIBUSB_DT_INTERFACE => "Interface", ::ffi::LIBUSB_DT_ENDPOINT => "Endpoint", ::ffi::LIBUSB_DT_BOS => "BOS", ::ffi::LIBUSB_DT_DEVICE_CAPABILITY => "Device Capability", ::ffi::LIBUSB_DT_HID => "HID", ::ffi::LIBUSB_DT_REPORT => "Report", ::ffi::LIBUSB_DT_PHYSICAL => "Physical", ::ffi::LIBUSB_DT_HUB => "HUB", ::ffi::LIBUSB_DT_SUPERSPEED_HUB => "Superspeed Hub", ::ffi::LIBUSB_DT_SS_ENDPOINT_COMPANION => "Superspeed Endpoint Companion", _ => "" } } fn get_bcd_version(bcd_version: u16) -> String { let digit1 = (bcd_version & 0xF000) >> 12; let digit2 = (bcd_version & 0x0F00) >> 8; let digit3 = (bcd_version & 0x00F0) >> 4; let digit4 = (bcd_version & 0x000F) >> 0; if digit1 > 0 { format!("{}{}.{}{}", digit1, digit2, digit3, digit4) } else { format!("{}.{}{}", digit2, digit3, digit4) } } fn get_class_type(class: u8) -> &'static str { match class { ::ffi::LIBUSB_CLASS_PER_INTERFACE => "(Defined at Interface level)", ::ffi::LIBUSB_CLASS_AUDIO => "Audio", ::ffi::LIBUSB_CLASS_COMM => "Comm", ::ffi::LIBUSB_CLASS_HID => "HID", ::ffi::LIBUSB_CLASS_PHYSICAL => "Physical", ::ffi::LIBUSB_CLASS_PRINTER => "Printer", ::ffi::LIBUSB_CLASS_IMAGE => "Image", ::ffi::LIBUSB_CLASS_MASS_STORAGE => "Mass Storage", ::ffi::LIBUSB_CLASS_HUB => "Hub", ::ffi::LIBUSB_CLASS_DATA => "Data", ::ffi::LIBUSB_CLASS_SMART_CARD => "Smart Card", ::ffi::LIBUSB_CLASS_CONTENT_SECURITY => "Content Security", ::ffi::LIBUSB_CLASS_VIDEO => "Video", ::ffi::LIBUSB_CLASS_PERSONAL_HEALTHCARE => "Personal Healthcare", ::ffi::LIBUSB_CLASS_DIAGNOSTIC_DEVICE => "Diagnostic Device", ::ffi::LIBUSB_CLASS_WIRELESS => "Wireless", ::ffi::LIBUSB_CLASS_APPLICATION => "Application", ::ffi::LIBUSB_CLASS_VENDOR_SPEC => "Vendor Specific", _ => "" } } fn get_endpoint(address: u8) -> String { let number = address & ::ffi::LIBUSB_ENDPOINT_ADDRESS_MASK; match address & ::ffi::LIBUSB_ENDPOINT_DIR_MASK { ::ffi::LIBUSB_ENDPOINT_IN => format!("EP {} IN", number), ::ffi::LIBUSB_ENDPOINT_OUT | _ => format!("EP {} OUT", number) } } fn get_transfer_type(attributes: u8) -> &'static str { match attributes & ::ffi::LIBUSB_TRANSFER_TYPE_MASK { ::ffi::LIBUSB_TRANSFER_TYPE_CONTROL => "Control", ::ffi::LIBUSB_TRANSFER_TYPE_ISOCHRONOUS => "Isochronous", ::ffi::LIBUSB_TRANSFER_TYPE_BULK => "Bulk", ::ffi::LIBUSB_TRANSFER_TYPE_INTERRUPT => "Interrupt", ::ffi::LIBUSB_TRANSFER_TYPE_BULK_STREAM => "Bulk Stream", _ => "" } } fn get_synch_type(attributes: u8) -> &'static str { match (attributes & ::ffi::LIBUSB_ISO_SYNC_TYPE_MASK) >> 2 { ::ffi::LIBUSB_ISO_SYNC_TYPE_NONE => "None", ::ffi::LIBUSB_ISO_SYNC_TYPE_ASYNC => "Async", ::ffi::LIBUSB_ISO_SYNC_TYPE_ADAPTIVE => "Adaptive", ::ffi::LIBUSB_ISO_SYNC_TYPE_SYNC => "Sync", _ => "" } } fn get_usage_type(attributes: u8) -> &'static str { match (attributes & ::ffi::LIBUSB_ISO_USAGE_TYPE_MASK) >> 4 { ::ffi::LIBUSB_ISO_USAGE_TYPE_DATA => "Data", ::ffi::LIBUSB_ISO_USAGE_TYPE_FEEDBACK => "Feedback", ::ffi::LIBUSB_ISO_USAGE_TYPE_IMPLICIT => "Implicit", _ => "" } } fn get_string_descriptor(handle: *mut ::ffi::libusb_device_handle, desc_index: u8) -> Option { if handle.is_null() || desc_index == 0{ return None } let mut vec = Vec::::with_capacity(256); let ptr = (&mut vec[..]).as_mut_ptr(); let len = unsafe { ::ffi::libusb_get_string_descriptor_ascii(handle, desc_index, ptr as *mut c_uchar, vec.capacity() as c_int) }; if len > 0 { unsafe { vec.set_len(len as usize) }; match String::from_utf8(vec) { Ok(s) => Some(s), Err(_) => None } } else { None } }