| use std::time::Duration; |
| |
| use rusb::{ |
| Context, Device, DeviceDescriptor, DeviceHandle, Direction, Result, TransferType, UsbContext, |
| }; |
| |
| #[derive(Debug)] |
| struct Endpoint { |
| config: u8, |
| iface: u8, |
| setting: u8, |
| address: u8, |
| } |
| |
| fn convert_argument(input: &str) -> u16 { |
| if input.starts_with("0x") { |
| return u16::from_str_radix(input.trim_start_matches("0x"), 16).unwrap(); |
| } |
| u16::from_str_radix(input, 10) |
| .expect("Invalid input, be sure to add `0x` for hexadecimal values.") |
| } |
| |
| fn main() { |
| let args: Vec<String> = std::env::args().collect(); |
| |
| if args.len() < 3 { |
| println!("usage: read_device <base-10/0xbase-16> <base-10/0xbase-16>"); |
| return; |
| } |
| |
| let vid = convert_argument(args[1].as_ref()); |
| let pid = convert_argument(args[2].as_ref()); |
| |
| match Context::new() { |
| Ok(mut context) => match open_device(&mut context, vid, pid) { |
| Some((mut device, device_desc, mut handle)) => { |
| read_device(&mut device, &device_desc, &mut handle).unwrap() |
| } |
| None => println!("could not find device {:04x}:{:04x}", vid, pid), |
| }, |
| Err(e) => panic!("could not initialize libusb: {}", e), |
| } |
| } |
| |
| fn open_device<T: UsbContext>( |
| context: &mut T, |
| vid: u16, |
| pid: u16, |
| ) -> Option<(Device<T>, DeviceDescriptor, DeviceHandle<T>)> { |
| let devices = match context.devices() { |
| Ok(d) => d, |
| Err(_) => return None, |
| }; |
| |
| for device in devices.iter() { |
| let device_desc = match device.device_descriptor() { |
| Ok(d) => d, |
| Err(_) => continue, |
| }; |
| |
| if device_desc.vendor_id() == vid && device_desc.product_id() == pid { |
| match device.open() { |
| Ok(handle) => return Some((device, device_desc, handle)), |
| Err(e) => panic!("Device found but failed to open: {}", e), |
| } |
| } |
| } |
| |
| None |
| } |
| |
| fn read_device<T: UsbContext>( |
| device: &mut Device<T>, |
| device_desc: &DeviceDescriptor, |
| handle: &mut DeviceHandle<T>, |
| ) -> Result<()> { |
| handle.reset()?; |
| |
| let timeout = Duration::from_secs(1); |
| let languages = handle.read_languages(timeout)?; |
| |
| println!("Active configuration: {}", handle.active_configuration()?); |
| println!("Languages: {:?}", languages); |
| |
| if !languages.is_empty() { |
| let language = languages[0]; |
| |
| println!( |
| "Manufacturer: {:?}", |
| handle |
| .read_manufacturer_string(language, device_desc, timeout) |
| .ok() |
| ); |
| println!( |
| "Product: {:?}", |
| handle |
| .read_product_string(language, device_desc, timeout) |
| .ok() |
| ); |
| println!( |
| "Serial Number: {:?}", |
| handle |
| .read_serial_number_string(language, device_desc, timeout) |
| .ok() |
| ); |
| } |
| |
| match find_readable_endpoint(device, device_desc, TransferType::Interrupt) { |
| Some(endpoint) => read_endpoint(handle, endpoint, TransferType::Interrupt), |
| None => println!("No readable interrupt endpoint"), |
| } |
| |
| match find_readable_endpoint(device, device_desc, TransferType::Bulk) { |
| Some(endpoint) => read_endpoint(handle, endpoint, TransferType::Bulk), |
| None => println!("No readable bulk endpoint"), |
| } |
| |
| Ok(()) |
| } |
| |
| fn find_readable_endpoint<T: UsbContext>( |
| device: &mut Device<T>, |
| device_desc: &DeviceDescriptor, |
| transfer_type: TransferType, |
| ) -> Option<Endpoint> { |
| for n in 0..device_desc.num_configurations() { |
| let config_desc = match device.config_descriptor(n) { |
| Ok(c) => c, |
| Err(_) => continue, |
| }; |
| |
| for interface in config_desc.interfaces() { |
| for interface_desc in interface.descriptors() { |
| for endpoint_desc in interface_desc.endpoint_descriptors() { |
| if endpoint_desc.direction() == Direction::In |
| && endpoint_desc.transfer_type() == transfer_type |
| { |
| return Some(Endpoint { |
| config: config_desc.number(), |
| iface: interface_desc.interface_number(), |
| setting: interface_desc.setting_number(), |
| address: endpoint_desc.address(), |
| }); |
| } |
| } |
| } |
| } |
| } |
| |
| None |
| } |
| |
| fn read_endpoint<T: UsbContext>( |
| handle: &mut DeviceHandle<T>, |
| endpoint: Endpoint, |
| transfer_type: TransferType, |
| ) { |
| println!("Reading from endpoint: {:?}", endpoint); |
| |
| let has_kernel_driver = match handle.kernel_driver_active(endpoint.iface) { |
| Ok(true) => { |
| handle.detach_kernel_driver(endpoint.iface).ok(); |
| true |
| } |
| _ => false, |
| }; |
| |
| println!(" - kernel driver? {}", has_kernel_driver); |
| |
| match configure_endpoint(handle, &endpoint) { |
| Ok(_) => { |
| let mut buf = [0; 256]; |
| let timeout = Duration::from_secs(1); |
| |
| match transfer_type { |
| TransferType::Interrupt => { |
| match handle.read_interrupt(endpoint.address, &mut buf, timeout) { |
| Ok(len) => { |
| println!(" - read: {:?}", &buf[..len]); |
| } |
| Err(err) => println!("could not read from endpoint: {}", err), |
| } |
| } |
| TransferType::Bulk => match handle.read_bulk(endpoint.address, &mut buf, timeout) { |
| Ok(len) => { |
| println!(" - read: {:?}", &buf[..len]); |
| } |
| Err(err) => println!("could not read from endpoint: {}", err), |
| }, |
| _ => (), |
| } |
| } |
| Err(err) => println!("could not configure endpoint: {}", err), |
| } |
| |
| if has_kernel_driver { |
| handle.attach_kernel_driver(endpoint.iface).ok(); |
| } |
| } |
| |
| fn configure_endpoint<T: UsbContext>( |
| handle: &mut DeviceHandle<T>, |
| endpoint: &Endpoint, |
| ) -> Result<()> { |
| handle.set_active_configuration(endpoint.config)?; |
| handle.claim_interface(endpoint.iface)?; |
| handle.set_alternate_setting(endpoint.iface, endpoint.setting)?; |
| Ok(()) |
| } |