diff --git a/examples/libusb_info.rs b/examples/libusb_info.rs index 6d46f5b..3759254 100644 --- a/examples/libusb_info.rs +++ b/examples/libusb_info.rs @@ -1,8 +1,20 @@ extern crate libusb_sys as ffi; +extern crate libc; use std::mem; use std::str; use std::ffi::CStr; +use std::mem::MaybeUninit; +use libc::{c_char, c_int}; + +#[link(name = "libusb-1.0")] +extern "C" fn call_libusb_log_cb(context: *mut ::ffi::libusb_context, log_level: c_int, log_message: *const c_char) { + if !context.is_null() { + println!("{} : {:?}", log_level, &log_message); + } else { + println!("No USB context"); + } +} fn main() { print_version(); @@ -21,14 +33,15 @@ fn print_version() { } fn print_capabilities() { - let mut context: *mut ::ffi::libusb_context = unsafe { mem::uninitialized() }; + let mut context_uninit: MaybeUninit<*mut ::ffi::libusb_context> = MaybeUninit::uninit(); // library must be initialized before calling libusb_has_capabililty() - match unsafe { ::ffi::libusb_init(&mut context) } { + match unsafe { ::ffi::libusb_init(context_uninit.as_mut_ptr()) } { 0 => (), e => panic!("libusb_init: {}", e) }; + let context = unsafe { context_uninit.assume_init() }; unsafe { ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_DEBUG); ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_INFO); @@ -37,6 +50,8 @@ fn print_capabilities() { ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_NONE); } + unsafe { ::ffi::libusb_set_log_cb(context, call_libusb_log_cb,::ffi::LIBUSB_LOG_LEVEL_DEBUG) }; + println!("has capability? {}", unsafe { ::ffi::libusb_has_capability(::ffi::LIBUSB_CAP_HAS_CAPABILITY) }); println!("has hotplug? {}", unsafe { ::ffi::libusb_has_capability(::ffi::LIBUSB_CAP_HAS_HOTPLUG) }); println!("has HID access? {}", unsafe { ::ffi::libusb_has_capability(::ffi::LIBUSB_CAP_HAS_HID_ACCESS) }); diff --git a/examples/list_devices.rs b/examples/list_devices.rs index 01a524e..f15d754 100644 --- a/examples/list_devices.rs +++ b/examples/list_devices.rs @@ -1,36 +1,46 @@ extern crate libusb_sys as ffi; extern crate libc; -use libc::{c_int,c_uchar}; +use libc::{c_int, c_uchar, c_char}; -use std::mem; -use std::ptr; -use std::slice; +use std::{mem::{MaybeUninit}, slice, ptr}; + +#[link(name = "libusb-1.0")] +extern "C" fn call_libusb_log_cb(_context: *mut ::ffi::libusb_context, log_level: c_int, log_message: *const c_char) { + println!("USB_CallBack - {:?} : {:?}", log_level, log_message); +} fn main() { - let mut context: *mut ::ffi::libusb_context = unsafe { mem::uninitialized() }; + let mut context_uninit: MaybeUninit<*mut ::ffi::libusb_context> = MaybeUninit::uninit(); - match unsafe { ::ffi::libusb_init(&mut context) } { + match unsafe { ::ffi::libusb_init(context_uninit.as_mut_ptr()) } { 0 => (), e => panic!("libusb_init: {}", get_error(e)) }; - + let context = unsafe { context_uninit.assume_init() }; + unsafe { + // ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_DEBUG); + // ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_INFO); + // ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_WARNING); + ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_ERROR); + } + unsafe { ::ffi::libusb_set_log_cb(context, call_libusb_log_cb,::ffi::LIBUSB_LOG_LEVEL_DEBUG) }; 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 mut device_list_uninit: MaybeUninit<*const *mut ffi::libusb_device> = MaybeUninit::uninit(); - let len = unsafe { ::ffi::libusb_get_device_list(context, &mut device_list) }; + let get_device_list_result = unsafe { ::ffi::libusb_get_device_list(context, device_list_uninit.as_mut_ptr()) }; - if len < 0 { - println!("libusb_get_device_list: {}", get_error(len as c_int)); + if get_device_list_result < 0 { + println!("libusb_get_device_list: {}", get_error(get_device_list_result as c_int)); return; } - - let devs = unsafe { slice::from_raw_parts(device_list, len as usize) }; + let device_list: *const *mut ffi::libusb_device = unsafe { device_list_uninit.assume_init() }; + let devs = unsafe { slice::from_raw_parts(device_list, get_device_list_result as usize) }; for dev in devs { display_device(dev); @@ -40,20 +50,20 @@ fn list_devices(context: *mut ::ffi::libusb_context) { } fn display_device(dev: &*mut ::ffi::libusb_device) { - let mut descriptor: ::ffi::libusb_device_descriptor = unsafe { mem::uninitialized() }; + let mut descriptor_uninit: MaybeUninit<::ffi::libusb_device_descriptor> = MaybeUninit::uninit(); 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) } { + let has_descriptor = match unsafe { ::ffi::libusb_get_device_descriptor(*dev, descriptor_uninit.as_mut_ptr()) } { 0 => true, _ => false }; - + let descriptor = unsafe { descriptor_uninit.assume_init() }; if unsafe { ::ffi::libusb_open(*dev, &mut handle) } < 0 { - println!("Couldn't open device, some information will be missing"); + println!("Couldn't open device, some information will be missing"); // luck of OS permissions usually handle = ptr::null_mut(); } @@ -63,7 +73,7 @@ fn display_device(dev: &*mut ::ffi::libusb_device) { print!(" ID {:04x}:{:04x}", descriptor.idVendor, descriptor.idProduct); } - print!(" {}", get_device_speed(speed)); + print!(", Speed : {}", get_device_speed(speed)); if has_descriptor { if descriptor.iManufacturer > 0 { @@ -88,16 +98,17 @@ fn display_device(dev: &*mut ::ffi::libusb_device) { } } - println!(""); + println!("\n--------------------------------------------------"); 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() }; + let mut descriptor_uninit: MaybeUninit<*const ::ffi::libusb_config_descriptor> = MaybeUninit::uninit(); - match unsafe { ::ffi::libusb_get_config_descriptor(*dev, i, &mut descriptor) } { + match unsafe { ::ffi::libusb_get_config_descriptor(*dev, i, descriptor_uninit.as_mut_ptr()) } { 0 => { + let descriptor = unsafe { descriptor_uninit.assume_init() }; let config = unsafe { &*descriptor }; let interfaces = unsafe { slice::from_raw_parts(config.interface, config.bNumInterfaces as usize) }; @@ -223,6 +234,7 @@ fn get_error(err: c_int) -> &'static str { fn get_device_speed(speed: c_int) -> &'static str { match speed { + ::ffi::LIBUSB_SPEED_SUPER_PLUS => "10000 Mbps", ::ffi::LIBUSB_SPEED_SUPER => "5000 Mbps", ::ffi::LIBUSB_SPEED_HIGH => " 480 Mbps", ::ffi::LIBUSB_SPEED_FULL => " 12 Mbps", diff --git a/examples/read_device.rs b/examples/read_device.rs index 2a469a8..468bb27 100644 --- a/examples/read_device.rs +++ b/examples/read_device.rs @@ -3,11 +3,11 @@ extern crate libc; use libc::{c_int,c_uint,c_uchar}; -use std::mem; use std::slice; use std::io::{Read,Cursor}; use std::str::FromStr; +use std::mem::MaybeUninit; #[derive(Debug)] struct Endpoint { @@ -20,19 +20,25 @@ struct Endpoint { fn main() { let args: Vec = std::env::args().collect(); - if args.len() < 3 { - println!("usage: show_device "); + if args.len() < 2 { + println!("usage: "); return; } let vid: u16 = FromStr::from_str(args[1].as_ref()).unwrap(); let pid: u16 = FromStr::from_str(args[2].as_ref()).unwrap(); - let mut context: *mut ::ffi::libusb_context = unsafe { mem::uninitialized() }; - let mut device_list: *const *mut ::ffi::libusb_device = unsafe { mem::uninitialized() }; + let mut context_uninit: MaybeUninit<*mut ffi::libusb_context> = MaybeUninit::uninit(); + let context: *mut ffi::libusb_context; - match unsafe { ::ffi::libusb_init(&mut context) } { - 0 => (), + // let mut device_list: *const *mut ::ffi::libusb_device = unsafe { mem::uninitialized() }; + let mut device_list_uninit: MaybeUninit<*const *mut ffi::libusb_device> = MaybeUninit::uninit(); + let device_list: *const *mut ffi::libusb_device; + + match unsafe { ::ffi::libusb_init(context_uninit.as_mut_ptr()) } { + 0 => { + context = unsafe { context_uninit.assume_init() } + }, e => panic!("libusb_init: {}", e) }; @@ -46,19 +52,22 @@ fn main() { let device = unsafe { ::ffi::libusb_get_device(handle) }; unsafe { ::ffi::libusb_ref_device(device) }; - if unsafe { ::ffi::libusb_get_device_list(context, &mut device_list) } >= 0 { + if unsafe { ::ffi::libusb_get_device_list(context, device_list_uninit.as_mut_ptr()) } >= 0 { + device_list = unsafe { device_list_uninit.assume_init() }; print_device_tree(device); println!(""); - unsafe { ::ffi::libusb_free_device_list(device_list, 1) }; } let languages = get_language_ids(handle); println!("Supported languages: {:?}", languages); - let mut active_config: c_int = unsafe { mem::uninitialized() }; - match unsafe { ::ffi::libusb_get_configuration(handle, &mut active_config) } { - 0 => println!("Active configuration: {}", active_config), + let mut active_config_uninit = MaybeUninit::::zeroed(); + match unsafe { ::ffi::libusb_get_configuration(handle, active_config_uninit.as_mut_ptr()) } { + 0 => { + let active_config = unsafe { active_config_uninit.assume_init() }; + println!("Active configuration: {}", active_config) + }, e => println!("libusb_get_configuration: {}", e) } println!(""); @@ -149,17 +158,21 @@ fn get_language_ids(handle: *mut ::ffi::libusb_device_handle) -> Vec { } fn find_readable_endpoint(device: *mut ::ffi::libusb_device, transfer_type: u8) -> Option { - let mut device_descriptor: ::ffi::libusb_device_descriptor = unsafe { mem::uninitialized() }; + let mut descriptor_uninit: MaybeUninit = MaybeUninit::uninit(); - match unsafe { ::ffi::libusb_get_device_descriptor(device, &mut device_descriptor) } { + match unsafe { ::ffi::libusb_get_device_descriptor(device, descriptor_uninit.as_mut_ptr()) } { 0 => { + let device_descriptor: ffi::libusb_device_descriptor = unsafe { descriptor_uninit.assume_init() }; + for i in 0..device_descriptor.bNumConfigurations { - let mut config_ptr: *const ::ffi::libusb_config_descriptor = unsafe { mem::uninitialized() }; + let configuration_config_ptr_uninit: MaybeUninit<*mut *const ffi::libusb_config_descriptor> = MaybeUninit::uninit(); - match unsafe { ::ffi::libusb_get_config_descriptor(device, i, &mut config_ptr) } { + match unsafe { ::ffi::libusb_get_config_descriptor(device, i, *configuration_config_ptr_uninit.as_ptr()) } { 0 => { - let config_descriptor = unsafe { &*config_ptr }; - let interfaces = unsafe { slice::from_raw_parts(config_descriptor.interface, config_descriptor.bNumInterfaces as usize) }; + let config_descriptor: *mut *const ffi::libusb_config_descriptor = unsafe { configuration_config_ptr_uninit.assume_init() }; + + let interfaces = unsafe { slice::from_raw_parts((*(*config_descriptor)).interface, + (*(*config_descriptor)).bNumInterfaces as usize) }; for iface in interfaces { let settings = unsafe { slice::from_raw_parts(iface.altsetting, iface.num_altsetting as usize) }; @@ -173,7 +186,7 @@ fn find_readable_endpoint(device: *mut ::ffi::libusb_device, transfer_type: u8) if is_input && matches_type { return Some(Endpoint { - config: config_descriptor.bConfigurationValue, + config: unsafe { (*(*config_descriptor)).bConfigurationValue }, iface: iface_descriptor.bInterfaceNumber, setting: iface_descriptor.bAlternateSetting, address: endpoint_descriptor.bEndpointAddress @@ -197,7 +210,7 @@ fn find_readable_endpoint(device: *mut ::ffi::libusb_device, transfer_type: u8) } fn read_endpoint(handle: *mut ::ffi::libusb_device_handle, device: *mut ::ffi::libusb_device, endpoint: Endpoint, transfer_type: u8) { - println!("Reading from endpoint: {:?}", endpoint); + println!("Reading from the endpoint: {:?}", endpoint); let has_kernel_driver = unsafe { if ::ffi::libusb_kernel_driver_active(handle, endpoint.iface as c_int) == 1 { @@ -222,17 +235,26 @@ fn read_endpoint(handle: *mut ::ffi::libusb_device_handle, device: *mut ::ffi::l match unsafe { ::ffi::libusb_claim_interface(handle, endpoint.iface as c_int) } { 0 => { - match unsafe { ::ffi::libusb_set_interface_alt_setting(handle, endpoint.iface as c_int, endpoint.setting as c_int) } { + match unsafe { ::ffi::libusb_set_interface_alt_setting(handle, + endpoint.iface as c_int, + endpoint.setting as c_int) } { 0 => { let mut vec = Vec::::with_capacity(256); let timeout: c_uint = 1000; - let mut transferred: c_int = unsafe { mem::uninitialized() }; + let mut transferred_uninit = MaybeUninit::::zeroed(); + let transferred: c_int; match transfer_type { ::ffi::LIBUSB_TRANSFER_TYPE_INTERRUPT => { - match unsafe { ::ffi::libusb_interrupt_transfer(handle, endpoint.address as c_uchar, (&vec[..]).as_ptr() as *mut c_uchar, vec.capacity() as c_int, &mut transferred, timeout) } { + match unsafe { ::ffi::libusb_interrupt_transfer(handle, + endpoint.address as c_uchar, + (&vec[..]).as_ptr() as *mut c_uchar, + vec.capacity() as c_int, + transferred_uninit.as_mut_ptr(), + timeout) } { 0 => { + transferred = unsafe { transferred_uninit.assume_init() }; unsafe { vec.set_len(transferred as usize) }; println!(" - read: {:?}", vec); }, @@ -240,8 +262,14 @@ fn read_endpoint(handle: *mut ::ffi::libusb_device_handle, device: *mut ::ffi::l } }, ::ffi::LIBUSB_TRANSFER_TYPE_BULK => { - match unsafe { ::ffi::libusb_bulk_transfer(handle, endpoint.address as c_uchar, (&vec[..]).as_ptr() as *mut c_uchar, vec.capacity() as c_int, &mut transferred, timeout) } { + match unsafe { ::ffi::libusb_bulk_transfer(handle, + endpoint.address as c_uchar, + (&vec[..]).as_ptr() as *mut c_uchar, + vec.capacity() as c_int, + transferred_uninit.as_mut_ptr(), + timeout) } { 0 => { + transferred = unsafe { transferred_uninit.assume_init() }; unsafe { vec.set_len(transferred as usize) }; println!(" - read: {:?}", vec); }, @@ -251,7 +279,7 @@ fn read_endpoint(handle: *mut ::ffi::libusb_device_handle, device: *mut ::ffi::l tt => println!(" - can't read endpoint with transfer type {}", tt) } }, - e => println!("libusb_set_interface_alt_setting: {}", e) + e => println!("libusb_set_interface_alt_setting: {}", e), } match unsafe { ::ffi::libusb_release_interface(handle, endpoint.iface as c_int) } { diff --git a/examples/read_device_in_loop.rs b/examples/read_device_in_loop.rs new file mode 100644 index 0000000..675d1cb --- /dev/null +++ b/examples/read_device_in_loop.rs @@ -0,0 +1,339 @@ +extern crate libusb_sys as ffi; +extern crate libc; + +use libc::{c_int,c_uint,c_uchar}; + +use std::slice; + +use std::io::{Read,Cursor}; +use std::str::FromStr; +use std::mem::MaybeUninit; +use std::ptr; + +#[derive(Debug)] +struct Endpoint { + config: u8, + iface: u8, + setting: u8, + address: u8 +} + +fn main() { + let args: Vec = std::env::args().collect(); + + if args.len() < 2 { + println!("usage: "); + return; + } + + println!("try parse : {}", args[1]); + let vid: u16 = FromStr::from_str(args[1].as_ref()).unwrap(); + println!("try parse : {}", args[2]); + let pid: u16 = FromStr::from_str(args[2].as_ref()).unwrap(); + + let mut context_uninit: MaybeUninit<*mut ffi::libusb_context> = MaybeUninit::uninit(); + let context: *mut ffi::libusb_context; + match unsafe { ::ffi::libusb_init(context_uninit.as_mut_ptr()) } { + 0 => { + context = unsafe { context_uninit.assume_init() } + }, + e => panic!("libusb_init: {}", e) + }; + // unsafe { + // ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_DEBUG); + // ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_INFO); + // ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_WARNING); + // ::ffi::libusb_set_debug(context, ::ffi::LIBUSB_LOG_LEVEL_ERROR); + // } + + let mut device_list_uninit: MaybeUninit<*const *mut ffi::libusb_device> = MaybeUninit::uninit(); + let get_device_list_result = unsafe { ::ffi::libusb_get_device_list(context, device_list_uninit.as_mut_ptr()) }; + if get_device_list_result < 0 { + println!("libusb_get_device_list: {}", get_device_list_result); + return; + } + let device_list: *const *mut ffi::libusb_device = unsafe { device_list_uninit.assume_init() }; + let devs = unsafe { slice::from_raw_parts(device_list, get_device_list_result as usize) }; + + for device in devs { + print_device_tree(*device); + println!(""); + let mut handle: *mut ffi::libusb_device_handle = ptr::null_mut(); + if unsafe { ffi::libusb_open(device.cast(), &mut handle) } < 0 { + println!("Couldn't open device [{:?}], some information will be missing", device); + continue; + } + + if !handle.is_null() { + match unsafe { ::ffi::libusb_reset_device(handle) } { + 0 => { + unsafe { ::ffi::libusb_set_auto_detach_kernel_driver(handle, 0) }; + + let device = unsafe { ::ffi::libusb_get_device(handle) }; + unsafe { ::ffi::libusb_ref_device(device) }; + + let languages = get_language_ids(handle); + println!("Supported languages: {:?}", languages); + + let mut active_config_uninit = MaybeUninit::::zeroed(); + match unsafe { ::ffi::libusb_get_configuration(handle, active_config_uninit.as_mut_ptr()) } { + 0 => { + let active_config = unsafe { active_config_uninit.assume_init() }; + println!("Active configuration: {}", active_config) + }, + e => println!("libusb_get_configuration: {}", e) + } + println!(""); + + match find_readable_endpoint(device, ::ffi::LIBUSB_TRANSFER_TYPE_INTERRUPT, vid, pid) { + Some(ep) => read_endpoint(handle, device, ep, ::ffi::LIBUSB_TRANSFER_TYPE_INTERRUPT), + None => println!("No readable interrupt endpoint") + } + println!(""); + + match find_readable_endpoint(device, ::ffi::LIBUSB_TRANSFER_TYPE_BULK, vid, pid) { + Some(ep) => read_endpoint(handle, device, ep, ::ffi::LIBUSB_TRANSFER_TYPE_BULK), + None => println!("No readable bulk endpoint") + } + + unsafe { ::ffi::libusb_unref_device(device) }; + }, + e => println!("libusb_reset_device: {}", e) + } + unsafe { ::ffi::libusb_close(handle) }; + } else { + println!("ERROR on opening by ffi::libusb_open(...)"); + } + + + } + unsafe { ::ffi::libusb_free_device_list(device_list, 1) }; + + + unsafe { ::ffi::libusb_exit(context) }; +} + +fn print_device_tree(device: *mut ::ffi::libusb_device) -> usize { + if device.is_null() { + return 0; + } + + let parent = unsafe { ::ffi::libusb_get_parent(device) }; + let depth = print_device_tree(parent); + + for _ in 0..depth { + print!(" "); + } + + let bus = unsafe { ::ffi::libusb_get_bus_number(device) }; + let address = unsafe { ::ffi::libusb_get_device_address(device) }; + + println!("Bus {:03} Device {:03}", bus, address); + + return depth + 1; +} + +fn get_language_ids(handle: *mut ::ffi::libusb_device_handle) -> Vec { + let mut buf = Vec::::with_capacity(255); + let len = unsafe { ::ffi::libusb_get_string_descriptor(handle, 0, 0, (&mut buf[..]).as_mut_ptr() as *mut c_uchar, buf.capacity() as c_int) }; + + let mut languages = Vec::::new(); + + if len >= 0 { + unsafe { buf.set_len(len as usize) }; + + if buf.len() >= 2 { + let num_languages = (buf.len() - 2) / 2; + languages.reserve(num_languages); + + let mut cursor = Cursor::new(buf); + cursor.set_position(2); + + for _ in 0..num_languages { + let mut bytes = Vec::::with_capacity(2); + + match cursor.read(unsafe { slice::from_raw_parts_mut((&mut bytes[..]).as_mut_ptr(), bytes.capacity()) }) { + Ok(len) => { + if len == 2 { + unsafe { bytes.set_len(len) }; + + let langid = (bytes[1] as u16) << 8 | (bytes[0] as u16); + languages.push(langid) + } + else { + return languages; + } + }, + Err(_) => return languages + } + } + } + } + else { + println!("libusb_get_string_descriptor: {}", len); + } + + languages +} + +fn find_readable_endpoint(device: *mut ::ffi::libusb_device, + transfer_type: u8, + vid: u16, + pid: u16) -> Option { + + let mut descriptor_uninit: MaybeUninit = MaybeUninit::uninit(); + + match unsafe { ::ffi::libusb_get_device_descriptor(device, descriptor_uninit.as_mut_ptr()) } { + 0 => { + let device_descriptor: ffi::libusb_device_descriptor = unsafe { descriptor_uninit.assume_init() }; + + println!("Check Device '{} : {}'", device_descriptor.idVendor, device_descriptor.idProduct); + if vid <= 0 && pid <= 0 && device_descriptor.idVendor != vid && device_descriptor.idProduct != pid { + // skip device + println!("Device '{:03} : {:03}' skipped, because doesn't equal to '{} : {}'", + device_descriptor.idVendor, device_descriptor.idProduct, vid , pid); + return None; + } + + + for i in 0..device_descriptor.bNumConfigurations { + + let configuration_config_ptr_uninit: MaybeUninit<*mut *const ffi::libusb_config_descriptor> = MaybeUninit::uninit(); + match unsafe { ::ffi::libusb_get_config_descriptor(device, i, *configuration_config_ptr_uninit.as_ptr()) } { + 0 => { + let config_descriptor: *mut *const ffi::libusb_config_descriptor = unsafe { configuration_config_ptr_uninit.assume_init() }; + + let interfaces = unsafe { + slice::from_raw_parts((*(*config_descriptor)).interface, + (*(*config_descriptor)).bNumInterfaces as usize) + }; + + for iface in interfaces { + let settings = unsafe { slice::from_raw_parts(iface.altsetting, iface.num_altsetting as usize) }; + + for iface_descriptor in settings { + let endpoints = unsafe { slice::from_raw_parts(iface_descriptor.endpoint, iface_descriptor.bNumEndpoints as usize) }; + + for endpoint_descriptor in endpoints { + let is_input = endpoint_descriptor.bEndpointAddress & ::ffi::LIBUSB_ENDPOINT_DIR_MASK == ::ffi::LIBUSB_ENDPOINT_IN; + let matches_type = endpoint_descriptor.bmAttributes & ::ffi::LIBUSB_TRANSFER_TYPE_MASK == transfer_type; + + if is_input && matches_type { + return Some(Endpoint { + config: unsafe { (*(*config_descriptor)).bConfigurationValue }, + iface: iface_descriptor.bInterfaceNumber, + setting: iface_descriptor.bAlternateSetting, + address: endpoint_descriptor.bEndpointAddress + }); + } + } + } + } + unsafe { ffi::libusb_free_config_descriptor(*config_descriptor) }; + }, + e => println!("libusb_get_config_descriptor: {}", e) + } + } + + None + }, + e => { + println!("libusb_get_device_descriptor: {}", e); + None + } + } +} + +fn read_endpoint(handle: *mut ::ffi::libusb_device_handle, device: *mut ::ffi::libusb_device, endpoint: Endpoint, transfer_type: u8) { + println!("Reading from the endpoint: {:?}", endpoint); + + let has_kernel_driver = unsafe { + if ::ffi::libusb_kernel_driver_active(handle, endpoint.iface as c_int) == 1 { + match ::ffi::libusb_detach_kernel_driver(handle, endpoint.iface as c_int) { + 0 => (), + e => println!("libusb_detach_kernel_driver: {}", e) + } + + true + } + else { + false + } + }; + + println!(" - kernel driver? {}", has_kernel_driver); + + match unsafe { ::ffi::libusb_set_configuration(handle, endpoint.config as c_int) } { + 0 => { + println!(" - max packet size: {}", unsafe { ::ffi::libusb_get_max_packet_size(device, endpoint.address as c_uchar) }); + println!(" - max iso packet size: {}", unsafe { ::ffi::libusb_get_max_iso_packet_size(device, endpoint.address as c_uchar) }); + + match unsafe { ::ffi::libusb_claim_interface(handle, endpoint.iface as c_int) } { + 0 => { + match unsafe { ::ffi::libusb_set_interface_alt_setting(handle, + endpoint.iface as c_int, + endpoint.setting as c_int) } { + 0 => { + let mut vec = Vec::::with_capacity(256); + let timeout: c_uint = 1000; + + let mut transferred_uninit = MaybeUninit::::zeroed(); + let transferred: c_int; + + match transfer_type { + ::ffi::LIBUSB_TRANSFER_TYPE_INTERRUPT => { + match unsafe { ::ffi::libusb_interrupt_transfer(handle, + endpoint.address as c_uchar, + (&vec[..]).as_ptr() as *mut c_uchar, + vec.capacity() as c_int, + transferred_uninit.as_mut_ptr(), + timeout) } { + 0 => { + transferred = unsafe { transferred_uninit.assume_init() }; + unsafe { vec.set_len(transferred as usize) }; + println!(" - read: {:?}", vec); + }, + e => println!("libusb_interrupt_transfer: {}", e) + } + }, + ::ffi::LIBUSB_TRANSFER_TYPE_BULK => { + match unsafe { ::ffi::libusb_bulk_transfer(handle, + endpoint.address as c_uchar, + (&vec[..]).as_ptr() as *mut c_uchar, + vec.capacity() as c_int, + transferred_uninit.as_mut_ptr(), + timeout) } { + 0 => { + transferred = unsafe { transferred_uninit.assume_init() }; + unsafe { vec.set_len(transferred as usize) }; + println!(" - read: {:?}", vec); + }, + e => println!("libusb_interrupt_transfer: {}", e) + } + }, + tt => println!(" - can't read endpoint with transfer type {}", tt) + } + }, + e => println!("libusb_set_interface_alt_setting: {}", e), + } + + match unsafe { ::ffi::libusb_release_interface(handle, endpoint.iface as c_int) } { + 0 => (), + e => println!("libusb_release_interface: {}", e) + } + }, + e => println!("libusb_claim_interface: {}", e) + } + }, + e => println!("libusb_set_configuration: {}", e) + } + + + + if has_kernel_driver { + match unsafe { ::ffi::libusb_attach_kernel_driver(handle, endpoint.iface as c_int) } { + 0 => (), + e => println!("libusb_attach_kernel_driver: {}", e) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 25ae199..c20dd2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,8 +2,9 @@ extern crate libc; -use libc::{c_void, c_int, c_uint, c_char, c_uchar, c_short, ssize_t, timeval}; +use std::convert::TryInto; +use libc::{c_char, c_int, c_short, c_uchar, c_uint, c_void, ssize_t, timeval}; #[repr(C)] pub struct libusb_context { @@ -126,6 +127,7 @@ pub struct libusb_bos_dev_capability_descriptor { pub bLength: u8, pub bDescriptorType: u8, pub bDevCapabilityType: u8, + pub dev_capability_data: [u8;0], } #[allow(non_snake_case)] @@ -135,6 +137,7 @@ pub struct libusb_bos_descriptor { pub bDescriptorType: u8, pub wTotalLength: u16, pub bNumDeviceCaps: u8, + pub dev_capability: *mut [libusb_bos_dev_capability_descriptor;0], } #[allow(non_snake_case)] @@ -169,6 +172,30 @@ pub struct libusb_container_id_descriptor { pub ContainerId: [u8; 16], } +#[allow(non_snake_case)] +#[repr(C)] +struct libusb_control_setup { + /// Request type. Bits 0:4 determine recipient, see + /// libusb_request_recipient. Bits 5:6 determine type, see + /// libusb_request_type. Bit 7 determines data transfer direction, see + /// libusb_endpoint_direction. + pub bmRequestType: u8, + /// Request. If the type bits of bmRequestType are equal to + /// libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + /// "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + /// libusb_standard_request. For other cases, use of this field is + /// application-specific. + pub bRequest: u8, + /// Value. Varies according to request + pub wValue: u16, + /// Index. Varies according to request, typically used to pass an index or offset + pub wIndex: u16, + /// Number of bytes to transfer + pub wLength: u16, +} + +pub const LIBUSB_CONTROL_SETUP_SIZE: usize = std::mem::size_of::(); + #[repr(C)] pub struct libusb_transfer { pub dev_handle: *mut libusb_device_handle, @@ -195,6 +222,7 @@ pub struct libusb_pollfd { pub type libusb_transfer_cb_fn = extern "C" fn(*mut libusb_transfer); pub type libusb_pollfd_added_cb = extern "C" fn(c_int, c_short, *mut c_void); pub type libusb_pollfd_removed_cb = extern "C" fn(c_int, *mut c_void); +pub type libusb_log_cb = extern "C" fn(*mut libusb_context, c_int/*libusb_log_level*/, *const c_char /*str to log?*/); // libusb_error pub const LIBUSB_SUCCESS: c_int = 0; @@ -240,6 +268,9 @@ pub const LIBUSB_LOG_LEVEL_WARNING: c_int = 2; pub const LIBUSB_LOG_LEVEL_INFO: c_int = 3; pub const LIBUSB_LOG_LEVEL_DEBUG: c_int = 4; +/// libusb_log_cb_mode +pub const LIBUSB_LOG_CB_GLOBAL: u8 = 1 << 0; +pub const LIBUSB_LOG_CB_CONTEXT: u8 = 1 << 1; // libusb_class_code pub const LIBUSB_CLASS_PER_INTERFACE: u8 = 0; @@ -268,6 +299,18 @@ pub const LIBUSB_SPEED_LOW: c_int = 1; pub const LIBUSB_SPEED_FULL: c_int = 2; pub const LIBUSB_SPEED_HIGH: c_int = 3; pub const LIBUSB_SPEED_SUPER: c_int = 4; +/// The device is operating at super speed plus (10000MBit/s). +pub const LIBUSB_SPEED_SUPER_PLUS: c_int = 5; + +// libusb_supported_speed +/// Low speed operation supported (1.5MBit/s). +pub const LIBUSB_LOW_SPEED_OPERATION: c_int = 1; +/// Full speed operation supported (12MBit/s). +pub const LIBUSB_FULL_SPEED_OPERATION: c_int = 2; +/// High speed operation supported (480MBit/s). +pub const LIBUSB_HIGH_SPEED_OPERATION: c_int = 4; +/// Superspeed operation supported (5000MBit/s). +pub const LIBUSB_SUPER_SPEED_OPERATION: c_int = 8; // libusb_descriptor_type @@ -357,6 +400,7 @@ extern "C" { pub fn libusb_init(context: *mut *mut libusb_context) -> c_int; pub fn libusb_exit(context: *mut libusb_context); pub fn libusb_set_debug(context: *mut libusb_context, level: c_int); + pub fn libusb_set_log_cb(context: *mut libusb_context, cd: libusb_log_cb, mode: c_int /*libusb_log_level*/); pub fn libusb_get_device_list(context: *mut libusb_context, list: *mut *const *mut libusb_device) -> ssize_t; pub fn libusb_free_device_list(list: *const *mut libusb_device, unref_devices: c_int); @@ -375,6 +419,7 @@ extern "C" { pub fn libusb_get_bus_number(dev: *const libusb_device) -> u8; pub fn libusb_get_port_number(dev: *mut libusb_device) -> u8; pub fn libusb_get_port_numbers(dev: *mut libusb_device, port_numbers: *mut u8, port_numbers_len: c_int) -> c_int; + pub fn libusb_get_port_path(context: *mut libusb_context, dev: *mut libusb_device, path: *mut u8, path_length: u8) -> c_int; pub fn libusb_get_device_address(dev: *const libusb_device) -> u8; pub fn libusb_get_device_speed(dev: *const libusb_device) -> c_int; pub fn libusb_get_max_packet_size(dev: *const libusb_device, endpoint: c_uchar) -> c_int; @@ -446,7 +491,219 @@ extern "C" { // defined as static inline in libusb.h -pub unsafe fn libusb_get_string_descriptor(dev_handle: *mut libusb_device_handle, desc_index: u8, langid: u16, data: *mut c_uchar, length: c_int) -> c_int -{ - libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING as u16) << 8 | desc_index as u16, langid, data, length as u16, 1000) +pub unsafe fn libusb_get_string_descriptor(dev_handle: *mut libusb_device_handle, + desc_index: u8, + langid: u16, + data: *mut c_uchar, + length: c_int) -> c_int { + libusb_control_transfer(dev_handle, + LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_STRING as u16) << 8 | desc_index as u16, + langid, data, + length as u16, + 1000) +} + +#[repr(C)] +union ToLe16 { + b8: [u8;2], + b16: u16 +} + +/// Convert a 16-bit value from host-endian to little-endian format. On +/// little endian systems, this function does nothing. On big endian systems, +/// the bytes are swapped. +/// x the host-endian value to convert +/// returns the value in little-endian byte order +pub fn libusb_cpu_to_le16(x: u16) -> u16 { + let _temp = ToLe16 { + b8: [(x >> 8) as u8,(x & 0xff) as u8] + }; + unsafe { _temp.b16 } } + +/// Get the data section of a control transfer. This convenience function is here +/// to remind you that the data does not start until 8 bytes into the actual +/// buffer, as the setup packet comes first. +/// +/// Calling this function only makes sense from a transfer callback function, +/// or situations where you have already allocated a suitably sized buffer at +/// transfer->buffer. +/// +/// param transfer a transfer +/// returns pointer to the first byte of the data section +pub unsafe fn libusb_control_transfer_get_data(transfer: *mut libusb_transfer) -> *mut c_uchar { + (*transfer).buffer.offset(LIBUSB_CONTROL_SETUP_SIZE as isize) +} + +#[allow(non_snake_case)] +/// Helper function to populate the setup packet (first 8 bytes of the data +/// buffer) for a control transfer. The wIndex, wValue and wLength values should +/// be given in host-endian byte order. +pub unsafe fn libusb_fill_control_setup(buffer: *mut c_uchar, + bmRequestType: u8, + bRequest: u8, + wValue: u16, + wIndex: u16, + wLength: u16) { + let setup: &mut libusb_control_setup = std::mem::transmute(buffer); + setup.bmRequestType = bmRequestType; + setup.bRequest = bRequest; + setup.wValue = libusb_cpu_to_le16(wValue); + setup.wIndex = libusb_cpu_to_le16(wIndex); + setup.wLength = libusb_cpu_to_le16(wLength); +} + +/// Helper function to populate the required \ref libusb_transfer fields +/// for a control transfer. +/// +/// If you pass a transfer buffer to this function, the first 8 bytes will +/// be interpreted as a control setup packet, and the wLength field will be +/// used to automatically populate the \ref libusb_transfer::length "length" +/// field of the transfer. Therefore the recommended approach is: +/// -# Allocate a suitably sized data buffer (including space for control setup) +/// -# Call libusb_fill_control_setup() +/// -# If this is a host-to-device transfer with a data stage, put the data +/// in place after the setup packet +/// -# Call this function +/// -# Call libusb_submit_transfer() +/// +/// It is also legal to pass a NULL buffer to this function, in which case this +/// function will not attempt to populate the length field. Remember that you +/// must then populate the buffer and length fields later. +pub unsafe fn libusb_fill_control_transfer(transfer: *mut libusb_transfer, + dev_handle: *mut libusb_device_handle, + buffer: *mut c_uchar, + callback: libusb_transfer_cb_fn, + user_data: *mut c_void, + timeout: c_uint) { + // struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; + let setup: *mut libusb_control_setup = std::mem::transmute(buffer); + (*transfer).dev_handle = dev_handle; + (*transfer).endpoint = 0; + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_CONTROL; + (*transfer).timeout = timeout; + (*transfer).buffer = buffer; + let ptr = setup as *const usize; + if !ptr.is_null() { + // let size_of_result = libusb_cpu_to_le16((*setup).wLength); + (*transfer).length = (LIBUSB_CONTROL_SETUP_SIZE + std::mem::size_of::()).try_into().unwrap(); + } + (*transfer).user_data = user_data; + (*transfer).callback = callback; +} + +/// Helper function to populate the required \ref libusb_transfer fields +/// for a bulk transfer. +/// +/// param transfer the transfer to populate +/// param dev_handle handle of the device that will handle the transfer +/// param endpoint address of the endpoint where this transfer will be sent +/// param buffer data buffer +/// param length length of data buffer +/// param callback callback function to be invoked on transfer completion +/// param user_data user data to pass to callback function +/// param timeout timeout for the transfer in milliseconds +pub unsafe fn libusb_fill_bulk_transfer(transfer: *mut libusb_transfer, + dev_handle: *mut libusb_device_handle, + endpoint: c_uchar, + buffer: *mut c_uchar, + length: c_int, + callback: libusb_transfer_cb_fn, + user_data: *mut c_void, + timeout: c_uint) { + (*transfer).dev_handle = dev_handle; + (*transfer).endpoint = endpoint; + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_BULK; + (*transfer).timeout = timeout; + (*transfer).buffer = buffer; + (*transfer).length = length; + (*transfer).user_data = user_data; + (*transfer).callback = callback; +} + +/// Helper function to populate the required \ref libusb_transfer fields +/// for a bulk transfer using bulk streams. +/// +/// Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 +/// +/// param transfer the transfer to populate +/// param dev_handle handle of the device that will handle the transfer +/// param endpoint address of the endpoint where this transfer will be sent +/// param stream_id bulk stream id for this transfer +/// param buffer data buffer +/// param length length of data buffer +/// param callback callback function to be invoked on transfer completion +/// param user_data user data to pass to callback function +/// param timeout timeout for the transfer in milliseconds +pub unsafe fn libusb_fill_bulk_stream_transfer(transfer: *mut libusb_transfer, + dev_handle: *mut libusb_device_handle, + endpoint: c_uchar, + stream_id: u32, + buffer: *mut c_uchar, + length: c_int, + callback: libusb_transfer_cb_fn, + user_data: *mut c_void, + timeout: c_uint) { + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, + length, callback, user_data, timeout); + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; + libusb_transfer_set_stream_id(transfer, stream_id); +} + +/// Helper function to populate the required \ref libusb_transfer fields +/// for an interrupt transfer. +/// +/// param transfer the transfer to populate +/// param dev_handle handle of the device that will handle the transfer +/// param endpoint address of the endpoint where this transfer will be sent +/// param buffer data buffer +/// param length length of data buffer +/// param callback callback function to be invoked on transfer completion +/// param user_data user data to pass to callback function +/// param timeout timeout for the transfer in milliseconds +pub unsafe fn libusb_fill_interrupt_transfer(transfer: *mut libusb_transfer, + dev_handle: *mut libusb_device_handle, + endpoint: c_uchar, + buffer: *mut c_uchar, + length: c_int, + callback: libusb_transfer_cb_fn, + user_data: *mut c_void, + timeout: c_uint) { + (*transfer).dev_handle = dev_handle; + (*transfer).endpoint = endpoint; + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + (*transfer).timeout = timeout; + (*transfer).buffer = buffer; + (*transfer).length = length; + (*transfer).user_data = user_data; + (*transfer).callback = callback; +} + +pub unsafe fn libusb_fill_iso_transfer(transfer: *mut libusb_transfer, + dev_handle: *mut libusb_device_handle, + endpoint: c_uchar, + buffer: *mut c_uchar, + length: c_int, + num_iso_packets: c_int, + callback: libusb_transfer_cb_fn, + user_data: *mut c_void, + timeout: c_uint) { + (*transfer).dev_handle = dev_handle; + (*transfer).endpoint = endpoint; + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + (*transfer).timeout = timeout; + (*transfer).buffer = buffer; + (*transfer).length = length; + (*transfer).num_iso_packets = num_iso_packets; + (*transfer).user_data = user_data; + (*transfer).callback = callback; +} + +pub unsafe fn libusb_set_iso_packet_lengths(transfer: *mut libusb_transfer, + length: c_uint) { + for one_descriptor in (*transfer).iso_packet_desc.iter_mut() { + one_descriptor.length = length; + } +} \ No newline at end of file