Created
August 25, 2023 14:24
-
-
Save will-hart/b49eaed1dd87c80fa6291bf9d71f195a to your computer and use it in GitHub Desktop.
PC to USB Device comms example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "pedalrs_gui" | |
version = "0.1.0" | |
authors = ["Will Hart <[email protected]>"] | |
edition = "2021" | |
rust-version = "1.56" | |
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |
[lib] | |
crate-type = ["cdylib", "rlib"] | |
[dependencies] | |
rusb = "0.9.0" | |
[profile.release] | |
opt-level = 2 # fast and small wasm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use crate::usb_device; | |
static VID: u16 = 0x16c0; | |
static PID: u16 = 0x27dd; | |
pub fn main() { | |
usb_device::send_message(VID, PID, 0x08).ok(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
} | |
pub fn send_message(vid: u16, pid: u16, command: u8) -> Result<bool> { | |
match Context::new() { | |
Ok(mut context) => match open_device(&mut context, vid, pid) { | |
Some((mut device, device_desc, mut handle)) => { | |
write_device(&mut device, &device_desc, &mut handle, command) | |
} | |
None => { | |
println!("could not find device {:04x}:{:04x}", vid, pid); | |
Ok(false) | |
} | |
}, | |
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(_) => continue, | |
} | |
} | |
} | |
None | |
} | |
fn write_device<T: UsbContext>( | |
device: &mut Device<T>, | |
device_desc: &DeviceDescriptor, | |
handle: &mut DeviceHandle<T>, | |
command: u8, | |
) -> Result<bool> { | |
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.len() > 0 { | |
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_writable_endpoint(device, device_desc, TransferType::Interrupt) { | |
Some(endpoint) => { | |
println!(" - Found interrupt mode on endpoint"); | |
write_endpoint(handle, endpoint, TransferType::Interrupt, command); | |
return Ok(true); | |
} | |
None => println!(" - No interrupt mode on endpoint"), | |
} | |
match find_writable_endpoint(device, device_desc, TransferType::Bulk) { | |
Some(endpoint) => { | |
println!(" - Found bulk mode on endpoint"); | |
write_endpoint(handle, endpoint, TransferType::Bulk, command); | |
return Ok(true); | |
} | |
None => println!(" - No bulk mode on endpoint"), | |
} | |
Ok(false) | |
} | |
fn find_writable_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_number, interface) in config_desc.interfaces().enumerate() { | |
for interface_desc in interface.descriptors() { | |
for (endpoint_number, endpoint_desc) in | |
interface_desc.endpoint_descriptors().enumerate() | |
{ | |
if endpoint_desc.direction() == Direction::Out | |
&& endpoint_desc.transfer_type() == transfer_type | |
{ | |
println!( | |
"Found writable endpoint {}:{} at address {} for device {}", | |
interface_number, | |
endpoint_number, | |
endpoint_desc.address(), | |
device.address() | |
); | |
return Some(Endpoint { | |
config: config_desc.number(), | |
iface: interface_desc.interface_number(), | |
setting: interface_desc.setting_number(), | |
address: endpoint_desc.address(), | |
}); | |
} | |
} | |
} | |
} | |
} | |
None | |
} | |
fn write_endpoint<T: UsbContext>( | |
handle: &mut DeviceHandle<T>, | |
endpoint: Endpoint, | |
transfer_type: TransferType, | |
command: u8, | |
) { | |
println!("Writing to 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 timeout = Duration::from_secs(1); | |
println!("Handle state {:?}", handle); | |
match transfer_type { | |
TransferType::Interrupt => { | |
match handle.write_interrupt(endpoint.address, &mut [0x00; 4], timeout) { | |
Ok(len) => { | |
println!(" - wrote: {} bytes", len); | |
} | |
Err(err) => { | |
println!("could not write to endpoint: {}", err); | |
} | |
} | |
} | |
TransferType::Bulk => { | |
match handle.write_bulk(endpoint.address, &mut [0x01, command], timeout) { | |
Ok(len) => { | |
println!(" - wrote {:?} bytes", len); | |
} | |
Err(err) => println!("could not write to 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<()> { | |
println!( | |
"Configuring for sending, and claiming the interface. {:?}", | |
endpoint | |
); | |
handle.set_active_configuration(endpoint.config)?; | |
handle.claim_interface(endpoint.iface)?; | |
handle.set_alternate_setting(endpoint.iface, endpoint.setting)?; | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment