Skip to content

Commit

Permalink
Update to new analyzer USB API.
Browse files Browse the repository at this point in the history
  • Loading branch information
martinling committed May 15, 2024
1 parent 2173663 commit 11bea09
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 33 deletions.
110 changes: 79 additions & 31 deletions src/backend/cynthion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ use nusb::{
const VID: u16 = 0x1d50;
const PID: u16 = 0x615b;

const MIN_SUPPORTED: u16 = 0x0002;
const NOT_SUPPORTED: u16 = 0x0003;
const CLASS: u8 = 0xff;
const SUBCLASS: u8 = 0x10;
const PROTOCOL: u8 = 0x00;

const ENDPOINT: u8 = 0x81;

Expand Down Expand Up @@ -80,10 +81,15 @@ impl State {
}
}

pub struct InterfaceSelection {
interface_number: u8,
alt_setting_number: u8,
}

/// Whether a Cynthion device is ready for use as an analyzer.
pub enum CynthionUsability {
/// Device is usable at supported speeds.
Usable(Vec<Speed>),
/// Device is usable via the given interface, at supported speeds.
Usable(InterfaceSelection, Vec<Speed>),
/// Device not usable, with a string explaining why.
Unusable(String),
}
Expand Down Expand Up @@ -111,33 +117,72 @@ pub struct CynthionStop {
worker: JoinHandle::<()>,
}

/// Check whether a Cynthion device is usable as an analyzer.
fn check_device(device_info: &DeviceInfo) -> Result<Vec<Speed>, Error>
/// Check whether a Cynthion device has an accessible analyzer interface.
fn check_device(device_info: &DeviceInfo)
-> Result<(InterfaceSelection, Vec<Speed>), Error>
{
// Check version is correct.
let version = device_info.device_version();
if !(MIN_SUPPORTED..=NOT_SUPPORTED).contains(&version) {
bail!("Device version not supported");
}

// Check we can open the device.
let device = device_info
.open()
.context("Failed to open device")?;

// Try to claim the interface.
let interface = device
.claim_interface(0)
.context("Failed to claim interface")?;
// Read the active configuration.
let config = device
.active_configuration()
.context("Failed to retrieve active configuration")?;

// Iterate over the interfaces...
for interface in config.interfaces() {
let interface_number = interface.interface_number();

// ...and alternate settings...
for alt_setting in interface.alt_settings() {
let alt_setting_number = alt_setting.alternate_setting();

// Ignore if this is not our supported target.
if alt_setting.class() != CLASS ||
alt_setting.subclass() != SUBCLASS
{
continue;
}

// Check protocol version.
let protocol = alt_setting.protocol();
if protocol != PROTOCOL {
bail!("Wrong protocol version: {} supported, {} found",
PROTOCOL, protocol);
}

// Try to claim the interface.
let interface = device
.claim_interface(interface_number)
.context("Failed to claim interface")?;

// Select the required alternate, if not the default.
if alt_setting_number != 0 {
interface
.set_alt_setting(alt_setting_number)
.context("Failed to select alternate setting")?;
}

// Fetch the available speeds.
let handle = CynthionHandle { interface };
let speeds = handle
.speeds()
.context("Failed to fetch available speeds")?;
// Fetch the available speeds.
let handle = CynthionHandle { interface };
let speeds = handle
.speeds()
.context("Failed to fetch available speeds")?;

// Now we have a usable device.
return Ok((
InterfaceSelection {
interface_number,
alt_setting_number,
},
speeds
))
}
}

// Now we have a usable device.
return Ok(speeds);
bail!("No supported analyzer interface found");
}

impl CynthionDevice {
Expand All @@ -147,9 +192,9 @@ impl CynthionDevice {
.filter(|info| info.product_id() == PID)
.map(|device_info|
match check_device(&device_info) {
Ok(speeds) => CynthionDevice {
Ok((iface, speeds)) => CynthionDevice {
device_info,
usability: Usable(speeds)
usability: Usable(iface, speeds)
},
Err(err) => CynthionDevice {
device_info,
Expand All @@ -162,9 +207,12 @@ impl CynthionDevice {

pub fn open(&self) -> Result<CynthionHandle, Error> {
match &self.usability {
Usable(..) => {
Usable(iface, _) => {
let device = self.device_info.open()?;
let interface = device.claim_interface(0)?;
let interface = device.claim_interface(iface.interface_number)?;
if iface.alt_setting_number != 0 {
interface.set_alt_setting(iface.alt_setting_number)?;
}
Ok(CynthionHandle { interface })
},
Unusable(reason) => bail!("Device not usable: {}", reason),
Expand All @@ -178,10 +226,10 @@ impl CynthionHandle {
use Speed::*;
let control = Control {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
recipient: Recipient::Interface,
request: 2,
value: 0,
index: 0,
index: self.interface.interface_number() as u16,
};
let mut buf = [0; 64];
let timeout = Duration::from_secs(1);
Expand Down Expand Up @@ -285,10 +333,10 @@ impl CynthionHandle {
fn write_state(&mut self, state: State) -> Result<(), Error> {
let control = Control {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
recipient: Recipient::Interface,
request: 1,
value: u16::from(state.0),
index: 0,
index: self.interface.interface_number() as u16,
};
let data = &[];
let timeout = Duration::from_secs(1);
Expand Down
4 changes: 2 additions & 2 deletions src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ impl DeviceSelector {
}
}
);
if let Usable(speeds) = &device.usability {
if let Usable(_, speeds) = &device.usability {
self.dev_speeds.push(
speeds.iter().map(Speed::description).collect()
)
Expand Down Expand Up @@ -220,7 +220,7 @@ impl DeviceSelector {
let device_id = self.dev_dropdown.selected();
let device = &self.devices[device_id as usize];
match &device.usability {
Usable(speeds) => {
Usable(_, speeds) => {
let speed_id = self.speed_dropdown.selected() as usize;
let speed = speeds[speed_id];
let cynthion = device.open()?;
Expand Down

0 comments on commit 11bea09

Please sign in to comment.