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<String> {
if handle.is_null() || desc_index == 0{
return None
}
let mut vec = Vec::<u8>::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
}
}